Swift: Obtain and Save the Updated Scnnode Over Time Using Projectpoint in Scenekit

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

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
}

Why does SCNNode move all over the place using ARKit?

The camera is likely losing its real world position reference when you shake the device.

iOS SceneKit to UIView projection issue

The problem here is that CGRect you use to calculate the mid points is based on the projected coordinates of the bounding box. The two corner points of the bounding box are transformed using the model view projection matrix, to get the correct view space coordinates for the mid points you need to perform the same transformation.

Hopefully the code is a bit clearer.

//world coordinates
let v1w = sm.node.convertPosition(sm.node.boundingBox.min, to: self.sceneView.scene?.rootNode)
let v2w = sm.node.convertPosition(sm.node.boundingBox.max, to: self.sceneView.scene?.rootNode)

//calc center of BB in world coordinates
let center = SCNVector3Make(
(v1w.x + v2w.x)/2,
(v1w.y + v2w.y)/2,
(v1w.z + v2w.z)/2)

//calc each mid point
let mp1w = SCNVector3Make(v1w.x, center.y, center.z)
let mp2w = SCNVector3Make(center.x, v2w.y, center.z)
let mp3w = SCNVector3Make(v2w.x, center.y, center.z)
let mp4w = SCNVector3Make(center.x, v1w.y, center.z)

//projected coordinates
let mp1p = self.sceneView.projectPoint(mp1w)
let mp2p = self.sceneView.projectPoint(mp2w)
let mp3p = self.sceneView.projectPoint(mp3w)
let mp4p = self.sceneView.projectPoint(mp4w)

var frameOld = sm.marker.frame

switch sm.position
{
case .Top:
frameOld.origin.y = CGFloat(mp1p.y) - frameOld.size.height/2
frameOld.origin.x = CGFloat(mp1p.x) - frameOld.size.width/2
sm.marker.isHidden = (mp1p.z < 0 || mp1p.z > 1)
case .Bottom:
frameOld.origin.y = CGFloat(mp2p.y) - frameOld.size.height/2
frameOld.origin.x = CGFloat(mp2p.x) - frameOld.size.width/2
sm.marker.isHidden = (mp2p.z < 0 || mp2p.z > 1)
case .Left:
frameOld.origin.y = CGFloat(mp3p.y) - frameOld.size.height/2
frameOld.origin.x = CGFloat(mp3p.x) - frameOld.size.width/2
sm.marker.isHidden = (mp3p.z < 0 || mp3p.z > 1)
case .Right:
frameOld.origin.y = CGFloat(mp4p.y) - frameOld.size.height/2
frameOld.origin.x = CGFloat(mp4p.x) - frameOld.size.width/2
sm.marker.isHidden = (mp4p.z < 0 || mp4p.z > 1)
}

It's a cool little sample project!

Points line up on box edges

Update on z-clipping issue

The projectPoint method returns a 3D SCNVector, the x any y coords as we know are the screen coordinates. The z coordinate tells us the location of the point relative to the far and near clipping planes (z = 0 near clipping plane, z = 1 far clipping plane). If you set a negative value for your near clipping plane, objects behind the camera would be rendered. We don't have a negative near clipping plane, but we also don't have any logic to say what happens if those projected point locations fall outside the z far and z near range.

I've updated the code above to include this zNear and zFar check and toggle the UIView visibility accordingly.

tl;dr

The markers visible when the camera was rotated 180deg are behind the camera, but they were still projected onto the view plane. And as we weren't checking if they we behind the camera, they were still displayed.

Scene Kit: projectPoint calculated is displaced

have to set z to zero on both world coordinates:

v1w.z = 0
v2w.z = 0

explaining things help

Mapping 3D SceneKit Node-Position to 2D Coordinate - projectPoint doesn't work

It could be that it's projecting from the untranslated, unmodified node position, or that the node is a child of another node, thus changing its frame of reference. Try using myNode.presentation.worldPosition as the input for projectPoint.

[EDIT]

Reading a bit closer, would you not be better off creating a flat texture of your sprite, attach it to the node in question, give it a 'billboard' constraint and set its Z order so that it always appears in front?



Related Topics



Leave a reply



Submit