How to swipe/slide go into the 3D Object - ios

I have done small demo in ARKit. i am facing problem with how to go inside 3D object by swiping.
For example, i have home 3D object, its working with Tap Gesture, Rotation Gesture, i would like to go inside home by swiping one to another room inside home 3D object.
Its rotating whole 3D object node, i could not able to swipe and go inside home..
This is the code i used for rotate based Y axis,
#objc private func viewRotated(_ gesture: UIRotationGestureRecognizer) {
let location = gesture.location(in: sceneView)
guard let node = node(at: location) else { return }
switch gesture.state {
case .began:
originalRotation = node.eulerAngles
case .changed:
guard var originalRotation = originalRotation else { return }
originalRotation.y -= Float(gesture.rotation)
node.eulerAngles = originalRotation
default:
originalRotation = nil
}
}
To rotate in all direction using UIPanGestureRecognizer, this is the code i have added,
#objc func viewPanned(gestureRecognize: UIPanGestureRecognizer){
let translation = gestureRecognize.translation(in: gestureRecognize.view!)
let x = Float(translation.x)
let y = Float(-translation.y)
let anglePan = sqrt(pow(x,2)+pow(y,2))*(Float)(M_PI)/180.0
var rotationVector = SCNVector4()
rotationVector.x = -y
rotationVector.y = x
rotationVector.z = 0
rotationVector.w = anglePan
homeNode?.rotation = rotationVector
if(gestureRecognize.state == UIGestureRecognizerState.ended) {
//
let currentPivot = homeNode?.pivot
let changePivot = SCNMatrix4Invert( (homeNode?.transform)!)
homeNode?.pivot = SCNMatrix4Mult(changePivot, currentPivot!)
homeNode?.transform = SCNMatrix4Identity
}
}
Perhaps there is another way of doing this, Can someone suggest a way..Thanks..

Related

Dragging SCNNode in ARKit Face Tracking

I want to be able to drag SCNNode on the face in real-time.
I try to map the touch position from the screen to the face's position but the node doesn't move as expected. The node moves slower than a finger and doesn't follow when I drag outside the user's face.
When dragging SCNNode to the left or the right, it seems to stick and can go further than just about half the face.
My current code:
#objc private func handleTouchGesture(_ sender: UIPanGestureRecognizer) {
let location = sender.location(in: arView)
switch sender.state {
case .began:
guard let nodeHitTest = arView.hitTest(location, options: nil).first
else { return }
lastDragPosition = nodeHitTest.localCoordinates
draggingNode = nodeHitTest.node
panStartZ = CGFloat(arView.projectPoint(nodeHitTest.node.position).z)
case .changed:
guard let lastDragPosition = lastDragPosition,
let draggingNode = draggingNode,
let panStartZ = panStartZ
else { return }
let localTouchPosition = arView.unprojectPoint(SCNVector3(location.x, location.y, panStartZ))
let movementVector = SCNVector3(
(localTouchPosition.x * -1.0) - lastDragPosition.x,
(localTouchPosition.y * -1.0),
0.0
)
draggingNode.localTranslate(by: movementVector)
self.lastDragPosition = localTouchPosition
case .ended:
(lastDragPosition, draggingNode, panStartZ) = (nil, nil, nil)
default:
break
}
}
I'm not sure if the issue is about the movementVector or not. I also try using several approaches but still not working.
Any help would be highly appreciated.
Thank you.

SceneKit finger panning becomes inaccurate as it moves away from center

In my ARSCNView, I'm drawing a SCNPlane with a grid inside. It is a child of sceneView.pointOfView. When you drag your finger across it, you can connect the dots in the grid. This works almost perfectly, except something subtly strange is happening: The edge of the tragged line SHOULD track accurately with the finger, but it actually falls ahead or behind depending on how far above or below the center it is respectively.
#objc func handlePan(gesture: UIPanGestureRecognizer) {
print("panned")
let location = gesture.location(in: sceneView)
let projectedOriginUI = sceneView.projectPoint(SCNVector3(0, 0, gridCon.grid.position.z))
let placeOnUI = sceneView.unprojectPoint(SCNVector3(location.x, location.y, CGFloat(projectedOriginUI.z)))
let rootNode: SCNNode = sceneView.scene.rootNode
let positionOnGrid = rootNode.convertPosition(placeOnUI, to: gridCon.grid)
let state = gesture.state
let hitVertex = gridCon.hitVertex(location: location, sceneView: sceneView)
if (state == UIGestureRecognizer.State.began) {
print("began")
if (hitVertex != nil) {
let edge = EdgeController.createEdge()
edge.position.x = hitVertex?.position.x ?? 0
edge.position.y = hitVertex?.position.y ?? 0
gridCon.grid.addChildNode(edge)
draggedLine = edge
startVertex = hitVertex
print("Edge added!")
}
}
if (state == UIGestureRecognizer.State.ended) {
draggedLine = nil
startVertex = nil
}
if (draggedLine != nil && startVertex !== nil) {
print("line height changed")
let _draggedLine = draggedLine!
let geo: SCNPlane = _draggedLine.geometry as! SCNPlane
let between = startVertex!.position.y - positionOnGrid.y
geo.height = CGFloat(between)
_draggedLine.position.y = startVertex!.position.y - (between/2)
}
}
Why is my finger tracking slightly off here?

ios - ARKit - How to change object position to another surface

I'm working with ARKit, I have a function to move the object like this:
var currentNode = SCNNode()
#objc func moveNode(_ gesture: UIPanGestureRecognizer) {
if !isRotating{
//1. Get The Current Touch Point
let currentTouchPoint = gesture.location(in: self.sceneView)
if gesture.state == .began {
guard let nodeHitTest = self.sceneView.hitTest(currentTouchPoint, options: nil).first else {return}
let nodeHit = nodeHitTest.node
currentNode = nodeHit
}
if gesture.state == .changed {
//2. Get The Next Feature Point Etc
guard let hitTest = self.sceneView.hitTest(currentTouchPoint, types: .existingPlane).first else { return }
//3. Convert To World Coordinates
let worldTransform = hitTest.worldTransform
//4. Set The New Position
let newPosition = SCNVector3(worldTransform.columns.3.x, worldTransform.columns.3.y, worldTransform.columns.3.z)
currentNode.simdPosition = float3(newPosition.x, newPosition.y, newPosition.z)
}
}
}
If my camera detect two surface, how can I move the object from current surface to another surface? Thanks all.

Using UIPanGesture to move node in ARScene

so I'm trying to set up a pan gesture to move a node within a sceneview to another area of the view. For example, if I place a cup on a flat surface I want the ability to be able to move the cup to another location on that surface. I have this so far from looking at other questions, but was still confused.
#objc func panGesture(sender: UIPanGestureRecognizer){
let sceneView = sender.view as! ARSCNView
let pannedLocation = sender.location(in: sceneView)
let hitTest = sceneView.hitTest(pannedLocation)
let state = sender.state
if(state == .failed || state == .cancelled){
return
}
if(state == .began){
let sceneHitTestResult = hitTest.first!
}
}
check out this answer for a full playground example Drag Object in 3D View
you need to add this line to viewDidLoad
sceneView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(panGesture(_:))))
The basic panGesture function is...
#objc func panGesture(_ gesture: UIPanGestureRecognizer) {
gesture.minimumNumberOfTouches = 1
let results = self.sceneView.hitTest(gesture.location(in: gesture.view), types: ARHitTestResult.ResultType.featurePoint)
guard let result: ARHitTestResult = results.first else {
return
}
let hits = self.sceneView.hitTest(gesture.location(in: gesture.view), options: nil)
if let tappedNode = hits.first?.node {
let position = SCNVector3Make(result.worldTransform.columns.3.x, result.worldTransform.columns.3.y, result.worldTransform.columns.3.z)
tappedNode.position = position
}
}

ARKit : Handle tap to show / hide a node

I am new to ARKit , and i am trying an example to create a SCNBox on tap location. What i am trying to do is on initial touch i create a box and on the second tap on the created box it should be removed from the scene. I am doing the hit test. but it keeps on adding the box. I know this is a simple task, but i am unable to do it
#objc func handleTap(sender: UITapGestureRecognizer) {
print("hande tapp")
guard let _ = sceneView.session.currentFrame
else { return }
guard let scnView = sceneView else { return }
let touchLocation = sender.location(in: scnView)
let hitTestResult = scnView.hitTest(touchLocation, types: [ .featurePoint])
guard let pointOfView = sceneView.pointOfView else {return}
print("point \(pointOfView.name)")
if hitTestResult.count > 0 {
print("Hit")
if let _ = pointOfView as? ARBox {
print("Box Available")
}
else {
print("Adding box")
let transform = hitTestResult.first?.worldTransform.columns.3
let xPosition = transform?.x
let yPosition = transform?.y
let zPosition = transform?.z
let position = SCNVector3(xPosition!,yPosition!,zPosition!)
basketCount = basketCount + 1
let newBasket = ARBox(position: position)
newBasket.name = "basket\(basketCount)"
self.sceneView.scene.rootNode.addChildNode(newBasket)
boxNodes.append(newBasket)
}
}
}
pointOfView of a sceneView, is the rootnode of your scene, which is one used to render your whole scene. For generic cases, it usually is an empty node with lights/ camera. I don't think you should cast it as ARBox/ or any type of SCNNode(s) for that matter.
What you probably can try is the logic below (hitResults are the results of your hitTest):
if hitResults.count > 0 {
if let node = hitResults.first?.node as SCNNode? (or ARBox) {
// node.removeFromParentNode()
// or make the node opaque if you don't want to remove
else {
// add node.

Resources