I created a SCNView in ViewController, and I have an ARFrame information, how do I set the SCNView camera node so that it is the same as the ARFrame camera ? I don't want to use ARSCNView.
ARSession can be run as standalone entity (it must not necessarily be connected to a view). Use the following code to get what you expect:
import SceneKit
import ARKit
class ViewController: UIViewController {
#IBOutlet var sceneKitView: SCNView!
let session = ARSession()
var arCameraTransform: simd_float4x4?
let scnCamera = SCNNode()
override func viewDidLoad() {
super.viewDidLoad()
sceneKitView.scene = SCNScene()
sceneKitView.backgroundColor = .black
let cylinder = SCNCylinder(radius: 0.1, height: 1)
cylinder.firstMaterial?.diffuse.contents = UIColor.red
let node = SCNNode(geometry: cylinder)
node.position.z = -0.75
sceneKitView.scene?.rootNode.addChildNode(node)
scnCamera.camera = SCNCamera()
sceneKitView.scene?.rootNode.addChildNode(scnCamera)
self.session.delegate = self
self.session.run(ARWorldTrackingConfiguration())
}
}
...
extension ViewController: ARSessionDelegate {
func session(_ session: ARSession, didUpdate frame: ARFrame) {
self.arCameraTransform = frame.camera.transform
scnCamera.simdTransform = self.arCameraTransform!
print(sceneKitView.pointOfView?.simdTransform.columns.3 as Any)
}
}
P.S.
This works for .landscape orientation...
Related
Trying to render a 2D image to show on plane instead of the white/colored plane I have have currently. The following code renders the plane as colored. Tried the following to
the 2D image appear over the colored plane instead. The image I am using is in Assets folder and is: PNG image - 966 KB. Looked/read/researched Apple Docs, StackOflow searching for how.
import UIKit
import SceneKit
import ARKit
import RealityKit
class ViewController: UIViewController, ARSCNViewDelegate {
#IBOutlet var sceneView: ARSCNView!
override func viewDidLoad() {
super.viewDidLoad()
// Set the view's delegate
sceneView.delegate = self
// Show statistics such as fps and timing information
sceneView.showsStatistics = true
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Create a session configuration
let configuration = ARImageTrackingConfiguration()
if let imageToTrack = ARReferenceImage.referenceImages(inGroupNamed: "BdayCardImages", bundle: Bundle.main) {
configuration.trackingImages = imageToTrack
configuration.maximumNumberOfTrackedImages = 1
}
// 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()
}
// MARK: - ARSCNViewDelegatemake
//this method finds and triggers image plane
func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: SCNNode) -> ARAnchor?
{
let node = SCNNode()
if let imageAnchor = anchor as? ARImageAnchor
{
let plane = SCNPlane(
width: imageAnchor.referenceImage.physicalSize.width,
height: imageAnchor.referenceImage.physicalSize.height
)
plane.firstMaterial?.diffuse.contents =
UIColor(red: 1, green: 1.3, blue: 0.5, alpha: 0.8)
let planeNode = SCNNode(geometry: plane)
if imageAnchor.referenceImage.name == "TamBDYCArd3"
{
if let bdayImage = ARImageAnchor(named: "assets/TamBDYCArd3") //Error here: 'Argument passed to call that takes no arguments'
{
if let imageNode = bdayImage.rootNode.childNode.first //Error here: Type of expression is ambiguous without more context'
{
imageNode.update(with: imageNode)
planeNode.addChildNode(imageNode)
if imageAnchor.referenceImage.name == "TamBDYCArd3" //the png image
{
if let bdayImage = ARImageAnchor(named: "assets/TamBDYCArd3")
{
if let imageNode = bdayImage.rootNode.childNode.first
{
imageNode.update(with: imageNode)
planeNode.addChildNode(imageNode)
planeNode.addChildNode(bdayImage)
}
}
}
}
return node
}
}
Any help is appreciated. I am a newb so thanks for your consideration and time.
I was expecting to render the 2D image over the plane. Thanks
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
}
}
I want to detect the 2D images using ARKit and RealityKit. I don't want to use SceneKit because many implementations based on RealityKit. I couldn't find any examples detecting images on RealityKit. I referred https://developer.apple.com/documentation/arkit/detecting_images_in_an_ar_experience sample code from apple. It uses Scenekit and ARSCNViewDelegate
let arConfiguration = ARWorldTrackingConfiguration()
arConfiguration.planeDetection = [.vertical, .horizontal]
arConfiguration.isLightEstimationEnabled = true
arConfiguration.environmentTexturing = .automatic
if let referenceImages = ARReferenceImage.referenceImages(inGroupNamed: "sanitzer", bundle: nil) {
arConfiguration.maximumNumberOfTrackedImages = 1
arConfiguration.detectionImages = referenceImages
}
self.session.run(arConfiguration, options: [.resetTracking, .removeExistingAnchors])
I have implemented ARSessionDelegate but not able to detect image?
func session(_ session: ARSession, didAdd anchors: [ARAnchor]) {
//how to capture image anchor?
}
func session(_ session: ARSession, didUpdate anchors: [ARAnchor]) {
//how to capture image anchor?
}
Apple has implemented ARSCNViewDelegate capture the detected images. What is the equivalent delegate for ARSCNViewDelegate in RealityKit? How to detect ARImageAnchor?
In ARKit/RealityKit project use the following code for session() instance methods:
import ARKit
import RealityKit
class ViewController: UIViewController, ARSessionDelegate {
func session(_ session: ARSession, didUpdate anchors: [ARAnchor]) {
guard let imageAnchor = anchors.first as? ARImageAnchor,
let _ = imageAnchor.referenceImage.name
else { return }
let anchor = AnchorEntity(anchor: imageAnchor)
// Add Model Entity to anchor
anchor.addChild(model)
arView.scene.anchors.append(anchor)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
arView.session.delegate = self
resetTrackingConfig()
}
func resetTrackingConfig() {
guard let refImg = ARReferenceImage.referenceImages(inGroupNamed: "Sub",
bundle: nil)
else { return }
let config = ARWorldTrackingConfiguration()
config.detectionImages = refImg
config.maximumNumberOfTrackedImages = 1
let options = [ARSession.RunOptions.removeExistingAnchors,
ARSession.RunOptions.resetTracking]
arView.session.run(config, options: ARSession.RunOptions(options))
}
}
And take into consideration – a folder for reference images (in .png or .jpg format) must have an extension .arresourcegroup.
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 added an ar function into my app to dectect our products. One object is working but i want to add multiple arobject files. I scanned some objects and added them into my ar recource group.
I created a product.sks and added a label + background.
My first question: How can i fix the label to one object? I have two arobjects at the moment so i need 2 different labels. How can i differ the correct label to an object?
My second question: At the moment my HU label is fixed and doesnt move when i'm moving my iPhone. I tried to change some positions etc. but its always fixed.
How can i make it move?
Thanks in advance. I hope my concern is detailed enough
import UIKit
import SceneKit
import ARKit
class ARViewController: UIViewController, ARSCNViewDelegate {
#IBOutlet var sceneView: ARSCNView!
override func viewDidLoad() {
super.viewDidLoad()
sceneView.delegate = self
sceneView.showsStatistics = true
let scene = SCNScene(named: "art.scnassets/scene.scn")!
sceneView.scene = scene
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let configuration = ARWorldTrackingConfiguration()
configuration.detectionObjects = ARReferenceObject.referenceObjects(inGroupNamed: "Module", bundle: Bundle.main)!
sceneView.session.run(configuration)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
sceneView.session.pause()
}
func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
let node = SCNNode()
return node
}
func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
let node = SCNNode()
if let objectAnchor = anchor as? ARObjectAnchor {
let plane = SCNPlane(width: CGFloat(objectAnchor.referenceObject.extent.x * 0.8), height: CGFloat(objectAnchor.referenceObject.extent.y * 0.5))
plane.cornerRadius = plane.width * 0.125
let displayScene = SKScene(fileNamed: "product")
plane.firstMaterial?.diffuse.contents = displayScene
plane.firstMaterial?.isDoubleSided = true
plane.firstMaterial?.diffuse.contentsTransform = SCNMatrix4Translate(SCNMatrix4MakeScale(1, -1, 1), 0, 1, 0)
let planeNode = SCNNode(geometry: plane)
planeNode.position = SCNVector3Make(objectAnchor.referenceObject.center.x, objectAnchor.referenceObject.center.y + 0.12, objectAnchor.referenceObject.center.z)
node.addChildNode(planeNode)
}
return node
}
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
}
}
Use anchor.referenceObject.name?
Move how? If you want it to always face the phone, apply billboard constraint.