I have the following piece of code that works perfectly on 11.4.1 but fails on 12
let background = DispatchQueue(label:"task")
var debugMeshNode = SCNNode()
let myKit = MyKit()
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
self.background.async {
let node = self.myKit.extractNode(anchor:anchor)
self.debugMeshNode.addChildNode(node) // no node added on UI in iOS12
}
}
func session(_ session: ARSession, didUpdate frame: ARFrame) {
self.background.async {
self.myKit.process(frame: frame)
}
}
Could anyone point my mistake here?
UPDATE
The code seems to work if I add a print statement in the block like so,
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
self.background.async {
let node = self.myKit.extractNode(anchor:anchor)
self.debugMeshNode.addChildNode(node) // no node added on UI in iOS12
print("sample")
}
}
Originally from here, I used this
func guaranteeMainThreadSynchronousExecution(_ block: () -> ()) {
if Thread.isMainThread {
block()
} else {
DispatchQueue.main.sync {
block()
}
}
}
and updated my code like so,
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
self.guaranteeMainThreadSynchronousExecution {
self.background.async {
let node = self.myKit.extractNode(anchor:anchor)
self.debugMeshNode.addChildNode(node) // no node added on UI in iOS12
}
}
}
Then it works flawlessly. Hope this helps someone.
Related
So session delegate allows me to get a frame that contains camera image + some details. But how can I get something similar from RealityKit. Like a frame of rendered objects + shadows without background?
I'd like to do my own post-frame rendering in metal. So have realityKit render nice meshes and then do adjustments to the resulting frame myself & render to my own surface.
Regards
Dariusz
TLDR
You can add a session delegate to RealityKit's ARView: arView.session.delegate = sessionDelegate
SwiftUI
The Following is a how you could implement a session delegate with SwiftUI:
class SessionDelegate<ARContainer: UIViewRepresentable>: NSObject, ARSessionDelegate {
var arVC: ARContainer
var arGameView : ARView?
init(_ control: ARContainer) {
self.arVC = control
}
func setARView(_ arView: ARView){
arGameView = arView // get access the arView
}
func session(_ session: ARSession, didUpdate anchors : [ARAnchor]) {}
func session(_ session: ARSession, didUpdate frame: ARFrame) {
print("frame", frame)
}
func session(_ session: ARSession, didAdd anchors: [ARAnchor]) {}
func session(_ session: ARSession, didRemove anchors: [ARAnchor]) {}
}
Make your delegate available to your UIViewRepresentable through the makeCoordinator function:
struct CameraARContainer: UIViewRepresentable {
func makeCoordinator() -> SessionDelegate<Self>{
SessionDelegate<Self>(self)
}
func makeUIView(context: Context) -> ARView {
let arView = ARGameView(frame: .zero)
arView.session.delegate = context.coordinator
context.coordinator.setARView(arView)
return arView
}
func updateUIView(_ uiView: ARView, context: Context) {}
}
I'm confused about this delegate method that is called when planeDetection is active. The method is being successfully called when plane is detected, but where does the returned SCNNode go? How do I access it?
func renderer(_ renderer: SCNSceneRenderer,
nodeFor anchor: ARAnchor) -> SCNNode?
For what it's worth, I'm using a custom ARCL library that allows me to place nodes by GPS coordinates. For some reason, with that framework this method does not seem to be firing upon detecting a plane. My delegates are set properly because the renderer(_:nodeFor:) method does get called - otherwise I would just use this method.
func renderer(_ renderer: SCNSceneRenderer,
didAdd node: SCNNode,
for anchor: ARAnchor)
Telling about new SCNNode, that renderer(_:nodeFor:) instance method generates for us, we pass this node to ARKit that tethers it with a corresponding anchor to thoroughly track its position.
The node, returned by renderer(_:nodeFor:) method, added as a child to SCNScene root node. Quite often renderer(_:nodeFor:) method is used when tracking ARFaceAnchors.
var specialNode: SCNNode?
func renderer(_ renderer: SCNSceneRenderer,
nodeFor anchor: ARAnchor) -> SCNNode? {
guard let sceneView = renderer as? ARSCNView, anchor is ARFaceAnchor
else { return nil }
let faceGeometry = ARSCNFaceGeometry(device: sceneView.device!)!
self.specialNode = SCNNode(geometry: faceGeometry)
return self.specialNode
}
...
func renderer(_ renderer: SCNSceneRenderer,
didUpdate node: SCNNode,
for anchor: ARAnchor) {
if let faceAnchor = anchor as? ARFaceAnchor,
let faceGeo = node.geometry as? ARSCNFaceGeometry {
faceGeo.update(from: faceAnchor.geometry)
}
}
Nevertheless, you can use renderer(_:nodeFor:) method with any desired anchor type.
I am trying to develop the ARKit3 facial recognition application. I want to make an application that supports multi-face recognition. I have made the following settings, but it does not work. Is it wrong with me?
override func viewDidLoad() {
super.viewDidLoad()
/// SetupDelegate
faceSCNView.delegate = self
faceSCNView.session.delegate = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
/// FaceTrackingConfiguration
let configuration = ARFaceTrackingConfiguration()
/// MaxNumberOfTrackedFaces = 2
configuration.maximumNumberOfTrackedFaces = 2
/// Run
faceSCNView.session.run(configuration)
}
Delegate
extension GameViewController : ARSCNViewDelegate {
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
print(anchor.sessionIdentifier , anchor.identifier , anchor.name)
if anchor is ARFaceAnchor {
print("renderer didAdd", anchor.identifier , anchor.name ?? "noname")
}
}
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
guard
let faceAnchor = anchor as? ARFaceAnchor
else { return}
print("renderer didUpdate", faceAnchor.identifier , faceAnchor.blendShapes[.mouthClose] ?? 0)
}
}
extension GameViewController : ARSessionDelegate {
func session(_ session: ARSession, didAdd anchors: [ARAnchor]) {
print(anchors.count)
for anchor in anchors where anchor is ARFaceAnchor {
let faceAnchor = anchor as! ARFaceAnchor
print("Session didAdd", faceAnchor.identifier , faceAnchor.blendShapes[.mouthClose] ?? 0)
}
}
func session(_ session: ARSession, didUpdate anchors: [ARAnchor]) {
for anchor in anchors where anchor is ARFaceAnchor {
let faceAnchor = anchor as! ARFaceAnchor
print("Session didUpdate", faceAnchor.identifier , faceAnchor.blendShapes[.mouthClose] ?? 0)
}
}
}
No matter how many people perform facial recognition together, there is only one recognized Anchor, the identifier is: CA831DB2-E078-45C3-9A1C-44F8459AA04F.
renderer didUpdate CA831DB2-E078-45C3-9A1C-44F8459AA04F 0.04505286
Session didUpdate CA831DB2-E078-45C3-9A1C-44F8459AA04F 0.04578292
renderer didUpdate CA831DB2-E078-45C3-9A1C-44F8459AA04F 0.04813192
Session didUpdate CA831DB2-E078-45C3-9A1C-44F8459AA04F 0.04813192
Session didUpdate CA831DB2-E078-45C3-9A1C-44F8459AA04F 0.04832877
Session didUpdate CA831DB2-E078-45C3-9A1C-44F8459AA04F 0.0484867
renderer didUpdate CA831DB2-E078-45C3-9A1C-44F8459AA04F 0.0484867
renderer didUpdate CA831DB2-E078-45C3-9A1C-44F8459AA04F 0.04869337
Session didUpdate CA831DB2-E078-45C3-9A1C-44F8459AA04F 0.04869337
Session didUpdate CA831DB2-E078-45C3-9A1C-44F8459AA04F 0.0489419
renderer didUpdate CA831DB2-E078-45C3-9A1C-44F8459AA04F 0.05000613
Session didUpdate CA831DB2-E078-45C3-9A1C-44F8459AA04F 0.05000613
renderer didUpdate CA831DB2-E078-45C3-9A1C-44F8459AA04F 0.05070856
Session didUpdate CA831DB2-E078-45C3-9A1C-44F8459AA04F 0.05031016
Session didUpdate CA831DB2-E078-45C3-9A1C-44F8459AA04F 0.05070856
renderer didUpdate CA831DB2-E078-45C3-9A1C-44F8459AA04F 0.05118915
Session didUpdate CA831DB2-E078-45C3-9A1C-44F8459AA04F 0.05093153
Sorry, it is my problem, because my mobile phone is iPhoneX, it is A11 chip, multi-face recognition is not supported.
The reason for this problem is that I used the iOS13 Beta 1 system at the time, but the maximum number of face recognitions supported during the run was 3, but only one person was actually supported. When I upgraded to iOS13 Beta 2, the update was shown to be the correct one.
I have a Dash button that works with Bluetooth low energy. I can scan and find it, connect to it and discover its services.
Now I want to listen for it to see if its button is pressed or not.but It seems that I have some serious problems with the part. I am kind of new in Swift, so If you please help me how to solve it, I really appreciate it.
this is my swift code:
import CoreBluetooth
import UIKit
struct DisplayPeripheral{
var peripheral: CBPeripheral?
var lastRSSI: NSNumber?
var isConnectable: Bool?
}
class PeripheralViewController: UIViewController {
#IBOutlet weak var statusLabel: UILabel!
#IBOutlet weak var bluetoothIcon: UIImageView!
#IBOutlet weak var scanningButton: ScanButton!
var centralManager: CBCentralManager?
var peripherals: [DisplayPeripheral] = []
var viewReloadTimer: Timer?
let BEAN_NAME = "Security Tag"
let BEAN_SCRATCH_UUID = CBUUID(string: "90946c81-e466-4a43-9974-949e465d35a1")
let BEAN_SERVICE_UUID = CBUUID(string: "00001c00-d102-11e1-9b23-000efb0000a7")
var selectedPeripheral: CBPeripheral?
#IBOutlet weak var tableView: UITableView!
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
//Initialise CoreBluetooth Central Manager
centralManager = CBCentralManager(delegate: self, queue: DispatchQueue.main)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
viewReloadTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(PeripheralViewController.refreshScanView), userInfo: nil, repeats: true)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
viewReloadTimer?.invalidate()
}
func updateViewForScanning(){
statusLabel.text = "Scanning BLE Devices..."
bluetoothIcon.pulseAnimation()
bluetoothIcon.isHidden = false
scanningButton.buttonColorScheme(true)
}
func updateViewForStopScanning(){
let plural = peripherals.count > 1 ? "s" : ""
statusLabel.text = "\(peripherals.count) Device\(plural) Found"
bluetoothIcon.layer.removeAllAnimations()
bluetoothIcon.isHidden = true
scanningButton.buttonColorScheme(false)
}
#IBAction func scanningButtonPressed(_ sender: AnyObject){
if centralManager!.isScanning{
centralManager?.stopScan()
updateViewForStopScanning()
}else{
startScanning()
}
}
func startScanning(){
peripherals = []
self.centralManager?.scanForPeripherals(withServices: nil, options: [CBCentralManagerScanOptionAllowDuplicatesKey: true])
updateViewForScanning()
let triggerTime = (Int64(NSEC_PER_SEC) * 10)
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(triggerTime) / Double(NSEC_PER_SEC), execute: { () -> Void in
if self.centralManager!.isScanning{
self.centralManager?.stopScan()
self.updateViewForStopScanning()
}
})
}
func refreshScanView()
{
if peripherals.count > 1 && centralManager!.isScanning{
tableView.reloadData()
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destinationViewController = segue.destination as? PeripheralConnectedViewController{
destinationViewController.peripheral = selectedPeripheral
}
}
}
extension PeripheralViewController: CBCentralManagerDelegate{
func centralManagerDidUpdateState(_ central: CBCentralManager){
//if (central.state == CBCentralManagerState.poweredOn){
startScanning()
//}else{
// do something like alert the user that ble is not on
//}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber){
for (index, foundPeripheral) in peripherals.enumerated(){
if foundPeripheral.peripheral?.identifier == peripheral.identifier{
peripherals[index].lastRSSI = RSSI
return
}
}
let isConnectable = advertisementData["kCBAdvDataIsConnectable"] as! Bool
if(peripheral.name == BEAN_NAME)
{
print(peripheral.name)
print(peripheral.identifier)
print("is?",isConnectable)
let displayPeripheral = DisplayPeripheral(peripheral: peripheral, lastRSSI: RSSI, isConnectable: isConnectable)
peripherals.append(displayPeripheral)
}
tableView.reloadData()
}
}
extension PeripheralViewController: CBPeripheralDelegate {
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
print("Error connecting peripheral: \(error?.localizedDescription)")
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral)
{
print("Peripheral connected")
performSegue(withIdentifier: "PeripheralConnectedSegue", sender: self)
peripheral.discoverServices(nil)
}
func peripheral(peripheral: CBPeripheral, didDiscoverServices error: NSError?)
{
print("jdbsud")
for service in peripheral.services!
{
let thisService = service as CBService
if service.uuid == BEAN_SERVICE_UUID {
peripheral.discoverCharacteristics(nil,for: thisService)
}
}
}
}
extension PeripheralViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = self.tableView.dequeueReusableCell(withIdentifier: "cell")! as! DeviceTableViewCell
cell.displayPeripheral = peripherals[indexPath.row]
cell.delegate = self
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return peripherals.count
}
}
extension PeripheralViewController: DeviceCellDelegate{
func connectPressed(_ peripheral: CBPeripheral) {
if peripheral.state != .connected {
selectedPeripheral = peripheral
peripheral.delegate = self
centralManager?.connect(peripheral, options: nil)
//you can listen to the commands here
}
}
}
I printed something in the func peripheral(peripheral: CBPeripheral, didDiscoverServices error: NSError?) to check if I even enter there or not, apparently I don't enter that part of the code.
after working hard on the problem, I myself was able to solve it.
after finding the services I should've added these two functions to the code and it works like a charm:
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
debugPrint("Enabling ...")
for characteristic in service.characteristics! {
let thisCharacteristic = characteristic as CBCharacteristic
debugPrint("Characteristic: ", thisCharacteristic.uuid)
if thisCharacteristic.uuid == BEAN_SCRATCH_UUID {
debugPrint("Set to notify: ", thisCharacteristic.uuid)
// Prepare to show data
self.peripheral.setNotifyValue(true, for: thisCharacteristic)
}
}
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if characteristic.uuid == BEAN_SCRATCH_UUID {
let content = String(data: characteristic.value!, encoding: String.Encoding.utf8)
debugPrint("Notified.")
}
}
I'm trying to make iAd banner. Console says that banner is loaded and fetched successfully, but I can't see it in the simulator. What am I doing wrong here?
class GameViewController: UIViewController, ADBannerViewDelegate {
var banner : ADBannerView = ADBannerView()
in viewDidLoad:
{
banner.delegate = self
banner.alpha = 0.0
NSNotificationCenter.defaultCenter().addObserver(self, selector: "bannerViewDidLoadAd", name: "banner", object: nil)
}
func bannerViewWillLoadAd(banner: ADBannerView!) {
println("banner tries to load")
}
func bannerViewDidLoadAd(banner: ADBannerView!) {
println("banner successfully fetched")
UIView.animateWithDuration(0.5, animations: {banner.alpha = 1.0})
UIView.commitAnimations()
}
func bannerViewActionShouldBegin(banner: ADBannerView!, willLeaveApplication willLeave: Bool) -> Bool {
return true
}
func bannerViewActionDidFinish(banner: ADBannerView!) {
println("banner is closed")
}
func bannerView(banner: ADBannerView!, didFailToReceiveAdWithError error: NSError!) {
println("banner failed too! Man...")
println(error.localizedDescription)
UIView.animateWithDuration(0.5, animations: {banner.alpha = 1.0})
}
in GameScene:
func iBanner() {
NSNotificationCenter.defaultCenter().postNotificationName("banner", object: nil)
}
override func didMoveToView(view: SKView) {
iBanner()
}