Save AR Functionality
public func save(_ uiView: ARView) {
var modelEntityDetailsDict: [String: [String]] = [:]
// Iterate through AR anchors
if let arAnchors = uiView.session.currentFrame?.anchors {
for arAnchor in arAnchors {
if let modelName = arAnchor.name,
let identifier = arAnchor.identifier {
// Retrieve model entity and transform details
if let anchorEntity = uiView.scene.findEntity(named: identifier) as? AnchorEntity,
let modelEntity = anchorEntity.children.first as? ModelEntity {
let transformDetails = [
modelEntity.transform.translation.debugDescription,
modelEntity.transform.rotation.debugDescription,
modelEntity.transform.scale.debugDescription
]
modelEntityDetailsDict["\(modelName)@\(identifier.uuidString)"] = transformDetails
}
}
}
}
// Save AR Anchors as WorldMap
let anchorDataURL = self.worldMapDir.appendingPathComponent("ARExperience.map")
uiView.session.getCurrentWorldMap { worldMap, error in
guard let worldMap = worldMap else { return }
do {
let anchorData = try NSKeyedArchiver.archivedData(withRootObject: worldMap, requiringSecureCoding: true)
try anchorData.write(to: anchorDataURL, options: [.atomic])
} catch {
fatalError("Debug: Can't save map: \(error.localizedDescription)")
}
}
// Save modelEntity transforms as JSON
let modelEntityDataURL = self.worldMapDir.appendingPathComponent("ARExperience.json")
do {
let jsonData = try JSONSerialization.data(withJSONObject: modelEntityDetailsDict, options: [])
try jsonData.write(to: modelEntityDataURL)
} catch {
print("Debug: \(error.localizedDescription)")
}
}
Summary: The save
method is responsible for saving the AR anchors and model entity transforms for AR persistence. It iterates through the AR anchors, retrieves the model entity and its transform details, and stores them in a dictionary. Then, it saves the AR anchors as a WorldMap using the getCurrentWorldMap
method. Finally, it saves the model entity transforms as JSON. This method allows the user to save the positions and transformations of virtual objects placed in the scene for future retrieval and AR experiences.
Detailed Explanation:
Let’s go through the save
function step by step:
- Create a dictionary
modelEntityDetailsDict
to store the details of the model entities' transformations. The keys of the dictionary will be a combination of the model name and the anchor identifier. - Iterate through the AR anchors present in the current AR frame.
- For each anchor, check if it has a name and an identifier. If both are available, proceed to retrieve the model entity and its transformation details.
- Using the anchor’s identifier, find the corresponding
AnchorEntity
in the AR scene using thefindEntity(named:)
method. Then, access the first child of theAnchorEntity
, assuming it is the model entity. - Retrieve the translation, rotation, and scale values of the model entity’s transformation and store them as an array of strings in the
transformDetails
variable. - Add an entry to the
modelEntityDetailsDict
dictionary using the combination of the model name and the anchor identifier as the key and thetransformDetails
as the value. - Define the URL for storing the AR anchors as a World Map. The file will be named “ARExperience.map” and stored in the
worldMapDir
directory. - Call
getCurrentWorldMap
on the AR session to obtain the current world map. - Inside the completion block, check if the
worldMap
is not nil. If it exists, archive the world map data usingNSKeyedArchiver
and write it to theanchorDataURL
. - If an error occurs during the archiving and writing process, handle it by reporting a fatal error.
- Define the URL for storing the model entity transforms as JSON. The file will be named “ARExperience.json” and stored in the
worldMapDir
directory. - Serialize the
modelEntityDetailsDict
dictionary into JSON data usingJSONSerialization.data(withJSONObject:options:)
. - Write the JSON data to the
modelEntityDataURL
. - If an error occurs during the serialization and writing process, print the error description for debugging purposes.
In summary, the save
function iterates through the AR anchors in the current frame, retrieves the transformation details of the associated model entities, and stores both the AR anchors as a World Map and the model entity transforms as a JSON file. This enables saving the state of the AR experience, allowing it to be reloaded and restored at a later time with the same spatial alignment and placed virtual objects.
Load AR Functionality
public func load(_ uiView: ARView) {
// Load AR Anchors
let anchorDataURL = self.worldMapDir.appendingPathComponent("ARExperience.map")
do {
let anchorData = try Data(contentsOf: anchorDataURL)
if let worldMap = try NSKeyedUnarchiver.unarchivedObject(ofClass: ARWorldMap.self, from: anchorData) {
let configuration = ARWorldTrackingConfiguration()
configuration.initialWorldMap = worldMap
uiView.session.run(configuration, options: [.resetTracking, .removeExistingAnchors])
}
} catch {
print("Debug: Can't load map: \(error.localizedDescription)")
}
// Load modelEntity transforms
let modelEntityDataURL = self.worldMapDir.appendingPathComponent("ARExperience.json")
do {
let jsonData = try Data(contentsOf: modelEntityDataURL)
if let modelEntityDetailsDict = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: [String]] {
for (key, transformDetails) in modelEntityDetailsDict {
let components = key.components(separatedBy: "@")
guard components.count == 2 else { continue }
let modelName = components[0]
let identifier = components[1]
let anchorEntity = AnchorEntity()
anchorEntity.name = identifier
uiView.scene.addAnchor(anchorEntity)
let modelEntity = try ModelEntity.loadModel(named: modelName + ".usdz")
modelEntity.transform.translation = parseVector3(transformDetails[0])
modelEntity.transform.rotation = parseQuaternion(transformDetails[1])
modelEntity.transform.scale = parseVector3(transformDetails[2])
anchorEntity.addChild(modelEntity)
for animation in modelEntity.availableAnimations {
modelEntity.playAnimation(animation.repeat())
}
}
} else {
print("Debug: Can't load map")
}
} catch {
print("Debug: \(error.localizedDescription)")
}
}
Summary: The load
method in the AR persistence code loads saved AR anchors and model entity transforms. It retrieves the ARWorldMap from the saved data and uses it to initialize the ARWorldTrackingConfiguration, which is then used to run the AR session. It also retrieves the model entity transform details from the JSON data and creates corresponding anchor entities with model entities in the AR scene. The method enables animations for the loaded model entities.
Detailed Explanation:
Let’s break down the load
functionality in detail:
- Define the URL for the stored AR anchors as a World Map. The file is named “ARExperience.map” and located in the
worldMapDir
directory. - Attempt to read the data from the
anchorDataURL
usingData(contentsOf:)
. - If the data is successfully loaded, try to unarchive it using
NSKeyedUnarchiver.unarchivedObject(ofClass:from:)
as an instance ofARWorldMap
. - If the unarchiving is successful and the
worldMap
exists, create a newARWorldTrackingConfiguration
and set itsinitialWorldMap
property to the loadedworldMap
. - Run the AR session with the new configuration, specifying options to reset the tracking and remove any existing anchors.
- If an error occurs during the loading process, print the error description for debugging purposes.
- Define the URL for the stored model entity transforms as a JSON file. The file is named “ARExperience.json” and located in the
worldMapDir
directory. - Attempt to read the data from the
modelEntityDataURL
usingData(contentsOf:)
. - If the data is successfully loaded, try to deserialize it using
JSONSerialization.jsonObject(with:options:)
as a dictionary with string keys and array values. - If the deserialization is successful and the
modelEntityDetailsDict
exists, iterate through its key-value pairs. - Split each key into components using the “@” separator. Ensure that there are exactly two components; otherwise, skip the current iteration.
- Extract the model name and identifier from the components.
- Create a new
AnchorEntity
and set its name to the identifier. - Add the
AnchorEntity
to the AR scene usinguiView.scene.addAnchor(_:)
. - Load the model entity using
ModelEntity.loadModel(named:)
, appending the model name with the ".usdz" extension. - Set the translation, rotation, and scale of the model entity’s transformation using the parsed vector and quaternion values from the
transformDetails
array. - Add the model entity as a child of the
AnchorEntity
. - Play all available animations of the model entity in a repeating manner.
- If the JSON deserialization fails or the
modelEntityDetailsDict
does not exist, print a debug message indicating the failure. - If an error occurs during the loading process, print the error description for debugging purposes.
In summary, the load
function loads the previously saved AR anchors from a World Map file and restores the corresponding model entity transforms from a JSON file. It sets up the AR session with the loaded world map, creates anchor entities, loads model entities, applies their transformations, and plays animations if available.
Share AR Experience
To share the AR experience with others, the generated .map
and .json
files can be shared. These files contain the necessary information to recreate the saved AR anchors and model entity transforms. By sharing these files, other users can load the same AR scene with the exact positions and transformations of virtual objects. This allows for a consistent and synchronized AR experience across multiple devices, enabling users to collaborate, explore, and interact with the same augmented environment. Whether it's for educational purposes, gaming, or remote assistance, sharing these files empowers users to share their AR creations and immerse others in their augmented reality world.