Hi. I display buildings in mixed immersive view. Right now the building appears in relation to the person when the view is opened. (world anchor)
To position the building precisely, I want to use object tracking. Set up a project following the wwdc object tracking session. That works well sort of...
With an object anchor, the 3D object related to the anchor disappears as soon as the Tracked object is out of view, and with the big objects you don't get the chance to look around.
I figure I need to give my 3D object a world anchor, and only have that world anchor update if a change in the object anchor is detected. how do I do that?
Preferable using the tools in Reality Composer pro (or very well explained, as I am new to code)
Hi @Ilskov
Your proposed solution will work. Here's how to implement it.
In Reality Composer Pro, create a new component named ObjectToWorldComponent
and add it to the entity with the object anchor component. Don't forget to save the file.
In Xcode, open Packages/RealityKitContent/Sources/RealityKitContent/ObjectToWorldComponent.swift
and remove the name property; we won't be using it.
Create a system (for more information on Entity Component System see Understanding RealityKit’s modular architecture) to transfer the contents and location of your object anchor to a world anchor.
struct ObjectToWorldSystem: System {
// Create a private component to hold component properties
// we don't want to expose in Reality Composer Pro.
struct PrivateObjectToWorldComponent: Component {
let worldAnchorEntity:AnchorEntity
}
let query = EntityQuery(where: .has(ObjectToWorldComponent.self))
public init(scene: RealityKit.Scene) { }
public func update(context: SceneUpdateContext) {
let entities = context.entities(matching: self.query, updatingSystemWhen: .rendering)
for entity in entities {
guard let objectAnchorEntity = entity as? AnchorEntity,
objectAnchorEntity.isAnchored else {continue}
let objectTransform = entity.transformMatrix(relativeTo: nil)
if let privateComponent = entity.components[PrivateObjectToWorldComponent.self] {
// Update the position of the world anchor when the object moves.
let modelPosition = entity.position(relativeTo: nil)
let worldAnchorPosition = privateComponent.worldAnchorEntity.position(relativeTo: nil)
let distance = distance(modelPosition, worldAnchorPosition)
// Set to 1cm
let moveThreshold:Float = 0.01
// The object moved more than 1cm. Update the world anchor.
if distance > moveThreshold {
let newWorldAnchorEntity = AnchorEntity(.world(transform: objectTransform))
let existingWorldAnchorEntity = privateComponent.worldAnchorEntity
existingWorldAnchorEntity.children.forEach { child in
newWorldAnchorEntity.addChild(child)
}
existingWorldAnchorEntity.parent?.addChild(newWorldAnchorEntity)
existingWorldAnchorEntity.removeFromParent()
entity.components.set(PrivateObjectToWorldComponent(worldAnchorEntity: newWorldAnchorEntity))
}
}
else {
// Move the contents from an object anchor to a world anchor
let worldAnchorEntity = AnchorEntity(.world(transform: objectTransform))
let privateComponent = PrivateObjectToWorldComponent(worldAnchorEntity: worldAnchorEntity)
entity.children.forEach { child in
worldAnchorEntity.addChild(child)
}
entity.parent?.addChild(worldAnchorEntity)
entity.components.set(privateComponent)
}
}
}
}
Register the system and component in your app's initializer. Put this code in the file generated by Xcode that ends in "App".
init() {
ObjectToWorldComponent.registerComponent()
ObjectToWorldSystem.registerSystem()
}
Add code to your view to start a SpatialTrackingSession to obtain a person's permission to query the location of world and object anchor entities.
Important: to help protect people’s privacy, visionOS limits app access to cameras and other sensors in Apple Vision Pro. You need to add an NSWorldSensingUsageDescription to your app’s information property list to provide a usage description that explains how your app uses the data these sensors provide.
struct ImmersiveView: View {
let spatialTrackingSession = SpatialTrackingSession()
var body: some View {
RealityView { content in
if let immersiveContentEntity = try? await Entity(named: "Immersive", in: realityKitContentBundle) {
content.add(immersiveContentEntity)
}
}
.task {
// Start a spatial tracking session to obtain permission to query the object and world anchor transforms.
let configuration = SpatialTrackingSession.Configuration(
tracking: [.object, .world])
await spatialTrackingSession.run(configuration)
}
}
}
Note: In Reality Composer Pro you can add ObjectToWorldComponent
to any entity that has an object anchor component and it will adopt the desired behavior.