Since ARKit3 it is possible to run a ARSession() that supports the back and front camera simultaneously.
For example this creates an ARConfiguration for the front camera that supports also Worldtracking.
// session for the front camera
let configuration = ARFaceTrackingConfiguration()
configuration.isWorldTrackingEnabled
sceneView.session.run(configuration)
This example creates a configuration for a back camera session with Face Tracking enabled:
// session for the back camera
let configuration = ARWorldTrackingConfiguration()
configuration.userFaceTrackingEnabled = true
sceneView.session.run(configuration)
I would like to create 2 independent ARConfigurations and ARSessions that run simultaneously. Like this:
So far i tried this code:
import UIKit
import SceneKit
import ARKit
class ViewController: UIViewController, ARSCNViewDelegate {
// back camera view
#IBOutlet var backView: ARSCNView!
// front camera view
#IBOutlet weak var frontView: ARSCNView!
override func viewDidLoad() {
super.viewDidLoad()
// Set the view's delegate
sceneView.delegate = self
frontView.delegate = self
// Create a new scene for back camera
let scene = SCNScene(named: "art.scnassets/ship.scn")!
backView.scene = scene
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// session for the back camera
let configuration = ARWorldTrackingConfiguration()
configuration.userFaceTrackingEnabled = true
backView.session.run(configuration)
// session for the front camera
guard ARFaceTrackingConfiguration.isSupported else { return }
let configurationFront = ARFaceTrackingConfiguration()
configurationFront.isLightEstimationEnabled = true
frontView.session.run(configurationFront, options: [])
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// Pause the view's session
sceneView.session.pause()
}
func session(_ session: ARSession, didFailWithError error: Error) {
// Present an error message to the user
}
func sessionWasInterrupted(_ session: ARSession) {
// Inform the user that the session has been interrupted, for example, by presenting an overlay
}
func sessionInterruptionEnded(_ session: ARSession) {
// Reset tracking and/or remove existing anchors if consistent tracking is required
}
}
The back camera stops its video feed while the front camera is working properly. Any chances to solve this ?
It would also be a solution to run one ARSession and one low level video capture session on the other camera but i am running into the same problems.
Related
I am having trouble loading four collada animations in a sequence. Using this code, the animations start at the same time and there is no sequence between each other. I would like to star with SU.dae then FTW.dae and so on, such as large animation using Xcode.
Does anyone know how to properly fix this issues?
import UIKit
import SceneKit
import ARKit
class ViewController: UIViewController, ARSCNViewDelegate {
#IBOutlet var sceneView: ARSCNView!
var animations = [String: CAAnimation]()
var Stand_Up:Bool = true
override func viewDidLoad() {
super.viewDidLoad()
// Set the view's delegate
sceneView.delegate = self
// Show statistics such as fps and timing information
sceneView.showsStatistics = true
// Create a new scene
let scene = SCNScene()
// Set the scene to the view
sceneView.scene = scene
// Loaf the DAE amimation
loadAnimations (sceneName: "art.scnassets/animation/SU.dae")
loadAnimations (sceneName: "art.scnassets/animation/FTW.dae")
loadAnimations (sceneName: "art.scnassets/animation/R.dae")
loadAnimations (sceneName: "art.scnassets/animation/BAE.dae")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Create a session configuration
let configuration = ARWorldTrackingConfiguration()
// Run the view's session
sceneView.session.run(configuration)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// Pause the view's session
sceneView.session.pause()
}
func loadAnimations (sceneName:String) {
// Load the character in the idle animation
let idleScene = SCNScene(named: sceneName)!
// This node will be parent of all the animation models
let node = SCNNode()
// Add all the child nodes to the parent node
for child in idleScene.rootNode.childNodes {
node.addChildNode(child)
}
// Set up some properties
node.position = SCNVector3(0, -1, -2)
node.scale = SCNVector3(0.2, 0.2, 0.2)
// Add the node to the scene
sceneView.scene.rootNode.addChildNode(node)
animateEntireNodeTreeOnce(mostRootNode: node)
}
func animateEntireNodeTreeOnce(mostRootNode node: SCNNode){
onlyAnimateThisNodeOnce(node)
for childNode in node.childNodes {
animateEntireNodeTreeOnce(mostRootNode: childNode)
}
}
func onlyAnimateThisNodeOnce(_ node: SCNNode) {
if node.animationKeys.count > 0 {
for key in node.animationKeys {
let animation = node.animation(forKey: key)!
animation.repeatCount = 1
animation.duration = 5;
animation.isRemovedOnCompletion = false
node.removeAllAnimations()
node.addAnimation(animation, forKey: key)
}
}
}
func session(_ session: ARSession, didFailWithError error: Error) {
// Present an error message to the user
}
func sessionWasInterrupted(_ session: ARSession) {
// Inform the user that the session has been interrupted, for example, by presenting an overlay
}
func sessionInterruptionEnded(_ session: ARSession) {
// Reset tracking and/or remove existing anchors if consistent tracking is required
}
}
My project has a few views before it call a ARview with a reality composer project. I enabled the coaching view in it using the demo code that apple provides. My problem is. If I make a button to go back from that view and call it again, the camera starts to flick from second to second. If I do it again, the flick increases until that, if I do it again, the program crashes. How can I make that view completely new, like the first time I loaded it? I’ve tried removing the anchor from the view.session. Tried pausing the ar session. Could anyone help me with that?
This is my code
import UIKit
import RealityKit
import ARKit
class ARHomeViewController: UIViewController {
#IBOutlet var arView: ARView!
#IBOutlet weak var coachingOverlay: ARCoachingOverlayView!
override func viewDidLoad()
{
super.viewDidLoad()
presentCoachingOverlay()
addScene()
}
/// Begins the coaching process that instructs the user's movement during
/// ARKit's session initialization.
func presentCoachingOverlay() {
coachingOverlay.session = arView.session
coachingOverlay.delegate = self
coachingOverlay.goal = .horizontalPlane
coachingOverlay.activatesAutomatically = false
self.coachingOverlay.setActive(true, animated: true)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// Prevent the screen from being dimmed to avoid interuppting the AR experience.
UIApplication.shared.isIdleTimerDisabled = true
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
arView?.session.pause()
}
func addScene()
{
guard let anchor = try? ExperienciaCasa.loadMenu() else { return }
anchor.generateCollisionShapes(recursive: true)
arView.scene.addAnchor(anchor)
}
}
I was thinking to do some modification to my existing AR app, and I wanted to split the view and add inside 2 ARSCNView in this way users can use the VR Card Box and have a different experience but Xcode is always returning me:
Session (0x102617d10): did fail with error: Error Domain=com.apple.arkit.error Code=102 "Required sensor failed."
So, I'm supposing that I can't run 2 ARSCNView sessions at the same time, or am I wrong?
The answer is: Yes, it's possible.
Use the following code to accomplish it:
import UIKit
import SceneKit
import ARKit
class ViewController: UIViewController, ARSCNViewDelegate {
#IBOutlet weak var sceneView: ARSCNView!
#IBOutlet weak var sceneView2: ARSCNView!
override func viewDidLoad() {
super.viewDidLoad()
sceneView.delegate = self
sceneView.showsStatistics = true
let scene = SCNScene(named: "art.scnassets/ship.scn")!
sceneView.scene = scene
sceneView.isPlaying = true
// Setup for sceneView2
sceneView2.scene = scene
sceneView2.showsStatistics = sceneView.showsStatistics
// Now sceneView2 starts receiving updates
sceneView2.isPlaying = true
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let configuration = ARWorldTrackingConfiguration()
sceneView.session.run(configuration)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
sceneView.session.pause()
}
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
DispatchQueue.main.async {
self.updateFrame()
}
}
func updateFrame() {
// Clone pointOfView for Second View
let pointOfView: SCNNode = (sceneView.pointOfView?.clone())!
// Determine Adjusted Position for Right Eye
let orientation: SCNQuaternion = pointOfView.orientation
let orientationQuaternion: GLKQuaternion = GLKQuaternionMake(orientation.x,
orientation.y,
orientation.z,
orientation.w)
let eyePos: GLKVector3 = GLKVector3Make(1.0, 0.0, 0.0)
let rotatedEyePos: GLKVector3 = GLKQuaternionRotateVector3(orientationQuaternion,
eyePos)
let rotatedEyePosSCNV: SCNVector3 = SCNVector3Make(rotatedEyePos.x,
rotatedEyePos.y,
rotatedEyePos.z)
let mag: Float = 0.064 // Interocular distance (in metres)
pointOfView.position.x += rotatedEyePosSCNV.x * mag
pointOfView.position.y += rotatedEyePosSCNV.y * mag
pointOfView.position.z += rotatedEyePosSCNV.z * mag
// Set PointOfView for SecondView
sceneView2.pointOfView = pointOfView
}
}
For more details look at this project on a GitHub.
No, or at least it’s possible that you will get errors all the time. It seems that when you use both an ARSCN and a ARSKVIEW View at the same time, a sensor error is presented. It may be due to privacy?
I'm trying to respond to changes in the user's location in an iOS app using MapBox, but didUpdateUserLocation is not being called. Why isn't didUpdateUserLocation being called?
ViewController.swift
import UIKit
import Mapbox
class ViewController: UIViewController, MGLMapViewDelegate {
#IBOutlet weak var upButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
let styleURL = NSURL(string: "mapbox://styles/jmeyers919/cj8w00yxvfrqr2rpehxd47up1") // MGLStyle.darkStyleURL()
let mapView = MGLMapView(frame: view.bounds, styleURL: styleURL as URL?)
mapView.delegate = self
mapView.logoView.isHidden = true
mapView.attributionButton.isHidden = true
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
// Note that we have changed the center coordinate to New York City for this guide
// mapView.setCenter(CLLocationCoordinate2D(latitude: 44.0475276, longitude: -123.08927319), zoomLevel: 16, animated: false)
mapView.userTrackingMode = .follow
mapView.showsUserLocation = true
view.addSubview(mapView)
self.view.bringSubview(toFront: self.upButton)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func mapView(mapView: AnyObject!, didUpdateUserLocation userLocation: AnyObject!) {
print("didUpdateUserLocation")
}
}
While the showsUserLocation property is set to YES, this method is called
whenever a new location update is received by the map view. This method is also
called if the map view’s user tracking mode is set to
MGLUserTrackingModeFollowWithHeading and the heading changes, or if it is set
to MGLUserTrackingModeFollowWithCourse and the course changes.
This method is not called if the application is currently running in the background. If you want to receive location updates while running in the
background, you must use the Core Location framework.
I have a simple question. If I wanted to start a game and place the board right in front of me:
gameBoard!.position = SCNVector3(0, 0, -0.6)
This works until I leave the game and come back again. Can I show the game board in exact same position in front of camera or 0.6m in front of me? I might have physically moved to another position.
If you want to reset you ARSession, you have to pause, remove all nodes and rerun your session by resetting tracking and removing anchors.
I made a reset button that does it whenever i want to reset:
#IBAction func reset(_ sender: Any) {
sceneView.session.pause()
sceneView.scene.rootNode.enumerateChildNodes { (node, stop) in
node.removeFromParentNode()
}
sceneView.session.run(configuration, options: [.resetTracking, .removeExistingAnchors])
}
Or put it in your session was interrupted function!
This should be possible using the option resetTracking when you call run on your ARSession again.
Example:
if let configuration = sceneView.session.configuration {
sceneView.session.run(configuration,
options: .resetTracking)
}
To reset ARSession in ARKit framework is quite easy:
class ViewController: UIViewController, ARSCNViewDelegate, ARSessionDelegate {
#IBOutlet var arView: ARSCNView!
#IBOutlet weak var sessionInfoLabel: UILabel!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = [.horizontal, .vertical]
arView.session.run(configuration)
arView.session.delegate = self
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
arView.session.pause()
}
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
guard let planeAnchor = anchor as? ARPlaneAnchor else {
return
}
let plane = Plane(anchor: planeAnchor, in: arView)
node.addChildNode(plane)
}
func sessionInterruptionEnded(_ session: ARSession) {
resetSessionTracking()
sessionInfoLabel.text = "ARSession's interruption has ended"
}
private func resetSessionTracking() {
let config = ARWorldTrackingConfiguration()
config.planeDetection = [.vertical, .horizontal]
arView.scene.rootNode.enumerateChildNodes { (childNode, _) in
childNode.removeFromParentNode()
}
arView.session.run(config, options: [.removeExistingAnchors,
.resetTracking, ])
}
}
Hope this helps.
"This works until I leave the game and come back again."
You can't track camera position in the background. Whenever your app goes to the background and camera turns off, you're losing your position, and sessionWasInterrupted(_:) will be called.
A session is interrupted when it fails to receive camera or motion
sensing data. Session interruptions occur whenever camera capture is
not available—for example, when your app is in the background or there
are multiple foreground apps—or when the device is too busy to process
motion sensor data.