swiftlearneer
swiftlearneer

Reputation: 330

Swift: Obtain and save the updated SCNNode over time using projectPoint in scenekit

I am trying to use projectPoint to get the 2D information of the updated SCNNode in scenekit and save them.

Based on ignotusverum's suggestion, I am able to save the SCNNode in to a path in a button.

     var lastPosition: CGPoint?
     func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
            guard anchor == currentFaceAnchor,
                let contentNode = selectedContentController.contentNode,
                contentNode.parent == node
                else { return }
            for (index, vertex) in vertices.enumerated() {
            let vertex = sceneView.projectPoint(node.convertPosition(SCNVector3(vertex), to: nil))
            let xVertex = CGFloat(vertex.x)
            let yVertex = CGFloat(vertex.y)
            Position = CGPoint(x: xVertex, y: yVertex)
           }
            selectedContentController.session = sceneView?.session
            selectedContentController.sceneView = sceneView
            selectedContentController.renderer(renderer, didUpdate: contentNode, for: anchor)
        }

Started saving via a start button:

    private var fpsTimer = Timer()
    private var currentCaptureFrame = 0
    @IBAction private func startPressed() {
        currentCaptureFrame = 0 //inital capture frame
        fpsTimer = Timer.scheduledTimer(withTimeInterval: 1/fps, repeats: true, block: {(timer) -> Void in self.recordData()})
    }

Saved them via a stop button clicks:

@IBAction private func stopPressed() {
    do {
        fpsTimer.invalidate() //turn off the timer
        let capturedData = captureData.map{$0.stringRepresentation}.joined(separator:"\(lastPosition)>")
        let dir: URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last! as URL
        let url = dir.appendingPathComponent("testing.txt")
        try capturedData.appendLineToURL(fileURL: url as URL)
    }
    catch {
        print("Could not write to file")
    }
}

So far works fine with the points being saved. The problem is that in the saved data, I realized the data is only saving one frame of the x and y vertices. For example:

[(411.0618591308594, 534.4215087890625), (410.7286071777344, 544.9381713867188), (411.5425720214844, 522.1063232421875), (412.0340881347656, 512.1854248046875),... 

[(411.0618591308594, 534.4215087890625), (410.7286071777344, 544.9381713867188), (411.5425720214844, 522.1063232421875), (412.0340881347656, 512.1854248046875)

The data is repeating with one frame rather than the period I want to save from the moment when I click start button to stop button.

My question is how to save the updated SCNNode over time from the moment when I click start button to stop button?

Thanks in advance!

Upvotes: 2

Views: 437

Answers (2)

A. Claesson
A. Claesson

Reputation: 559

If I understand your question correctly you want to save the vertex positions each time they are updated, keeping track of all previous updates as well as the most recent one. In order to do this you simply need to append the new vertex position array to the global array with saved data.

 var savedPositions = [CGPoint]()
 var beginSaving = false

 func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
        guard anchor == currentFaceAnchor,
            let contentNode = selectedContentController.contentNode,
            contentNode.parent == node
            else { return }
        for vertex in vertices {
            let projectedPoint = sceneView.projectPoint(node.convertPosition(SCNVector3(vertex), to: nil))
            if beginSaving {
               savedPositions.append(CGPoint(x: projectedPoint.x, y: projectedPoint.y))
            }
        }
        selectedContentController.session = sceneView?.session
        selectedContentController.sceneView = sceneView
        selectedContentController.renderer(renderer, didUpdate: contentNode, for: anchor)
}

@IBAction private func startPressed() {
    beginSaving = true
}

@IBAction private func stopPressed() {
    beginSaving = false
    ....//do whatever with your array of saved positions
}

Upvotes: 1

Voltan
Voltan

Reputation: 1213

SceneKit calls this method exactly once per frame. If you do stuff inside the renderer, you gotta "get it done" and get out, otherwise it can have an impact on FPS.

If you need to move or check a lot of loop stuff, you can use timers to perform those efforts separately and that allows you some control over it. You can still keep your FPS up and break loops up or chunk work in the timers. If you do this, just make sure you put timers in the main thread.

You might also look at: node.presentation, ie: properties reflect the transitory values determined by any in-flight animations currently affecting the node.

Upvotes: 1

Related Questions