Thank you for the response! I'm new to Swift so I would definitely like assistance adapting the code. I see how it fits into the make closure of my RealityView
. How can I provide the planeEntity
and cubeEntity
created in appModel.updatePlane
to the DidAddEntity
subscriber? Is it just as easy as adding the plane and cube entities to variables in appModel - appModel.currentPlaneRendered
for example? This is my ImmersiveView
View:
struct ImmersiveView: View {
@Environment(AppModel.self) var appModel
@State private var updateFacingPlaneTaskWorking: Task<Void, Never>? = nil
var body: some View {
RealityView { content in
content.add(appModel.setupContentEntity())
// Add the initial RealityKit content
if let immersiveContentEntity = try? await Entity(named: "Immersive", in: realityKitContentBundle) {
content.add(immersiveContentEntity)
}
// Updates and renders the horizontal plane in front of the person at 10 Hz.
updateFacingPlaneTaskWorking = run(appModel.updateFacingPlaneWorking, withFrequency: 10)
// New content subscriber to the creation of a cubeEntity to rotate it after animation becomes available
_ = content.subscribe(to: SceneEvents.DidAddEntity.self, on: cubeEntity) { event in
let rotationY = simd_quatf(angle: Float(45.0 * .pi/180.0), axis: SIMD3(x: 0, y: 1, z: 0))
let cubeTransform = Transform(rotation: rotationY)
event.entity.move(to: cubeTransform, relativeTo: planeEntity, duration: TimeInterval(10), timingFunction: .easeInOut)
}
}
.onDisappear {
updateFacingPlaneTaskWorking?.cancel()
}
.task {
await appModel.runARKitSession()
}
.task {
await appModel.processPlaneDetectionUpdates()
}
}
}
I've adapted the code from the Room Tracking example for wall tracking into horizontal plane tracking with the PlaneDetectionProvider
. The planeEntity and cubeEntity creation occurs in the task where I call await appModel.processPlaneDetectionUpdates()
.
In appModel
I have the following functions:
func processPlaneDetectionUpdates() async {
for await anchorUpdate in planeData.anchorUpdates {
switch anchorUpdate.event {
case .added, .updated:
await updatePlane(anchorUpdate.anchor)
case .removed:
await removePlane(anchorUpdate.anchor)
}
}
}
@MainActor
func updatePlane(_ anchor: PlaneAnchor) async {
// Get the transform matrix of the anchor
let anchorTransform = anchor.originFromAnchorTransform
// Convert the matrix into a RealityKit Transform
var transform = Transform(matrix: anchorTransform)
// Add 6 inches twice (0.1524 meters) to the y-axis
transform.translation.y += 0.1524
entityMap[anchor.id]?.transform = transform
// getting anchor geometry
let anchorGeometry = anchor.geometry
guard let planeMeshResource = anchorGeometry.asMeshResource() else {
return
}
let planeEntity = ModelEntity(mesh: planeMeshResource, materials: [whiteMaterial])
planeEntity.transform = Transform(matrix: anchor.originFromAnchorTransform)
planeEntity.name = anchor.classification.description
// adding a collision component with a shape
guard let shape = try? await ShapeResource.generateStaticMesh(from: planeMeshResource) else {
logger.error("Failed to create ShapeResource from planeEntity geometry.")
return
}
planeEntity.collision = CollisionComponent(shapes: [shape], isStatic: true)
planeEntity.components.set(InputTargetComponent())
planeEntity.components.set(HoverEffectComponent())
if let planeCentroid = planeEntity.centroid {
// Create a cube at the centroid
let cubeMesh = MeshResource.generateBox(size: 0.1) // Create a cube with side length of 0.1 meters
let cubeMaterial = SimpleMaterial(color: .blue, isMetallic: false)
let cubeEntity = ModelEntity(mesh: cubeMesh, materials: [cubeMaterial])
// Add a collision component to the cube
let cubeShape = ShapeResource.generateBox(size: [0.1, 0.1, 0.1])
cubeEntity.collision = CollisionComponent(shapes: [cubeShape])
cubeEntity.position = planeCentroid
// cubeEntity.position.y += 0.3048
planeEntity.addChild(cubeEntity)
// Check if the cube intersects the plane using bounding boxes
if checkBoundingBoxOverlap(planeEntity: planeEntity, cubeEntity: cubeEntity) {
print("Cube intersects with the plane!")
} else {
print("No intersection detected.")
}
// add plane classification text
let font = UIFont.systemFont(ofSize: 0.2) // Adjust the font size to control text appearance
let textMesh = MeshResource.generateText(
anchor.classification.description,
extrusionDepth: 0.06, // Adjust depth to control thickness
font: font
)
let textEntity = ModelEntity(mesh: textMesh)
textEntity.name = anchor.classification.description
// Add white color material to the text
textEntity.model?.materials = [whiteMaterial]
// Add the BillboardComponent to the entity
textEntity.components.set(BillboardComponent())
textEntity.components.set(HoverEffectComponent())
// Calculate the bounding box of the text mesh
if let bounds = textEntity.model?.mesh.bounds {
// Offset the text position to center it
let offset = (bounds.max + bounds.min) / 2
textEntity.position = planeCentroid + offset
}
planeEntity.addChild(textEntity)
}
colliderPlanesRoot.addChild(planeEntity)
}