Skip to content

Instantly share code, notes, and snippets.

@ynagatomo
Created November 11, 2023 23:56
Show Gist options
  • Save ynagatomo/71cc6b22d0ba733f62ea8858bff13eae to your computer and use it in GitHub Desktop.
Save ynagatomo/71cc6b22d0ba733f62ea8858bff13eae to your computer and use it in GitHub Desktop.
A sample BillboardComponent for RealityKit in visionOS
// BillboardComponent.swift
import ARKit
import RealityKit
import SwiftUI
public struct BillboardComponent: Component, Codable {
public init() {}
}
public struct BillboardSystem: System {
static let query = EntityQuery(where: .has(BillboardComponent.self))
private let arkitSession = ARKitSession()
private let worldTrackingProvider = WorldTrackingProvider()
public init(scene: RealityKit.Scene) {
setUpSession()
}
func setUpSession() {
Task {
do {
try await arkitSession.run([worldTrackingProvider])
} catch {
print("Error: \(error)")
}
}
}
public func update(context: SceneUpdateContext) {
let entities = context.scene.performQuery(Self.query).map({ $0 })
guard !entities.isEmpty,
let deviceAnchor = worldTrackingProvider.queryDeviceAnchor(atTimestamp:
CACurrentMediaTime()) else { return }
let cameraTransform = Transform(matrix: deviceAnchor.originFromAnchorTransform)
for entity in entities {
let translation = entity.transform.translation
entity.look(at: cameraTransform.translation,
from: entity.position(relativeTo: nil),
relativeTo: nil,
forward: .positiveZ)
entity.transform.translation = translation
}
}
}
// BillboardComponentTestApp.swift
import SwiftUI
@main
struct BillboardComponentTestApp: App {
init() {
BillboardComponent.registerComponent()
BillboardSystem.registerSystem()
}
var body: some Scene {
WindowGroup {
ContentView()
}
ImmersiveSpace(id: "ImmersiveSpace") {
ImmersiveView()
}.immersionStyle(selection:
.constant(.mixed), in: .mixed)
}
}
// ContentView.swift
import SwiftUI
import RealityKit
import RealityKitContent
struct ContentView: View {
@State private var showImmersiveSpace = false
@State private var immersiveSpaceIsShown = false
@Environment(\.openImmersiveSpace) var openImmersiveSpace
@Environment(\.dismissImmersiveSpace) var dismissImmersiveSpace
var body: some View {
VStack {
Model3D(named: "Scene", bundle: realityKitContentBundle)
.padding(.bottom, 50)
Text("Hello, world!")
Toggle("Show Immersive Space", isOn: $showImmersiveSpace)
.toggleStyle(.button)
.padding(.top, 50)
}
.padding()
.onChange(of: showImmersiveSpace) { _, newValue in
Task {
if newValue {
switch await openImmersiveSpace(id: "ImmersiveSpace") {
case .opened:
immersiveSpaceIsShown = true
case .error, .userCancelled:
fallthrough
@unknown default:
immersiveSpaceIsShown = false
showImmersiveSpace = false
}
} else if immersiveSpaceIsShown {
await dismissImmersiveSpace()
immersiveSpaceIsShown = false
}
}
}
}
}
#Preview(windowStyle: .automatic) {
ContentView()
}
// ImmersiveView.swift
import SwiftUI
import RealityKit
import RealityKitContent
struct ImmersiveView: View {
var body: some View {
RealityView { content, attachments in
if let entity = attachments.entity(for: "Board") {
entity.components[BillboardComponent.self] = BillboardComponent()
entity.position = SIMD3<Float>(0, 1, -2)
content.add(entity)
// Add an ImageBasedLight for the immersive content here
// Put skybox here
}
} attachments: {
Attachment(id: "Board") {
Text("Billboard")
.font(.system(size: 100))
.padding(64)
.background(.green)
}
}
}
}
#Preview {
ImmersiveView()
.previewLayout(.sizeThatFits)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment