Found a workaround by using the Billboard ECS from the VisionOS 1.0 version SwiftSplash. Came across the source on stack overflow. I made a few naming changes, but it fixes the issue.
Is there a better solution to this?
https://stackoverflow.com/questions/60577468/how-to-implement-a-billboard-effect-lookat-camera-in-realitykit
//
// BillboardAttachmentFixSystem.swift
//
// Created by Justin Leger on 1/3/25.
//
import Foundation
import ARKit
import RealityKit
import SwiftUI
import simd
import OSLog
/// An ECS system that points all entities containing a billboard component at the camera.
public struct BillboardAttachmentFixSystem: System {
static let query = EntityQuery(where: .has(BillboardAttachmentFixComponent.self))
private let arkitSession = ARKitSession()
private let worldTrackingProvider = WorldTrackingProvider()
public init(scene: RealityKit.Scene) {
setupARKitSession()
}
func setupARKitSession() {
Task {
do {
try await arkitSession.run([worldTrackingProvider])
} catch {
os_log(.info, "Error: \(error)")
}
}
}
public func update(context: SceneUpdateContext) {
let entities = context.scene.performQuery(Self.query).map({ $0 })
guard !entities.isEmpty,
let pose = worldTrackingProvider.queryDeviceAnchor(atTimestamp: CACurrentMediaTime()) else { return }
let cameraTransform = Transform(matrix: pose.originFromAnchorTransform)
for entity in entities {
entity.look(at: cameraTransform.translation,
from: entity.scenePosition,
relativeTo: nil,
forward: .positiveZ)
}
}
}
/// The component that marks an entity as a billboard object which will always face the camera.
public struct BillboardAttachmentFixComponent: Component, Codable {
public init() {}
}
/// Entity extension holding convenience accessors and mutators for the components
/// this system uses. Components are stored in the `components` dictionary using the
/// component class (`.self`) as the key. This adds calculated properties to allow setting
/// and getting these components.
extension Entity {
/// Property for getting or setting an entity's `MockThruBillboardComponent`.
var mockThruBillboardComponent: BillboardAttachmentFixComponent? {
get { components[BillboardAttachmentFixComponent.self] }
set { components[BillboardAttachmentFixComponent.self] = newValue }
}
}