Volumetric windows
Now that we can import a Blender file to RealityKit, let's make it look fancy in it's own volumetric window.
Do a pushup! (Vision Pro demo)
Initial plain WindowGroup #
In your app's root view, apply the plain
windowStyle to the main WindowGroup:
WindowGroup {
ContentView()
}
.windowStyle(.plain)
This removes the usual window's background. To make our own, smaller view, we set up ContentView
.
struct ContentView: View {
@Environment(\.openWindow) var openWindow
var body: some View {
Text("Do a pushup!")
.font(.extraLargeTitle)
.padding(50)
.glassBackgroundEffect()
.onAppear {
openWindow(id: "model")
}
}
}
This view has some text, with padding and a glass background. It also has an onAppear
method that runs openWindow
.
This openWindow
method comes from the @Environment(\.openWindow)
setting. It's trying to open a window with id
of model
. We need to add that.
Volumetric WindowGroup #
Back in the root file, alongside the plain WindowGroup, add a second WindowGroup:
WindowGroup(id: "model") {
ModelView()
}
.windowStyle(.volumetric)
.defaultSize(width: 1, height: 1, depth: 1, in: .meters)
This sets the windowStyle
to volumetric
and gives it a size of 1m
cubed. It also displays a ModelView
. We should add that.
Set up ModelView #
Based on the code from this post, I set up a standalone ModelView
:
import SwiftUI
import RealityKit
import RealityKitContent
struct ModelView: View {
var body: some View {
RealityView { content in
if let animatedModel = loadEntity() {
content.add(animatedModel)
animatedModel.isEnabled = true
}
} update: { content in
guard let character = content.entities.first else { return }
character.isEnabled = true
character.availableAnimations.forEach { animation in
character.playAnimation(animation.repeat())
}
}
}
private func loadEntity() -> Entity? {
let entity = try? Entity.load(named: "Scene", in: realityKitContentBundle)
entity?.setScale(SIMD3(x: 0.1, y: 0.1, z: 0.1), relativeTo: nil)
entity?.setPosition(SIMD3(x: 0, y: -0.25, z: 0), relativeTo: nil)
return entity
}
}
A couple of small changes here, in the loadEntity
function. I adjusted setScale
to 0.1
to make the model a big bigger. I also added setPosition
with a y
value of -0.25
to move the model down a little. Play around with values that suit your setup.
Launch in simulator #
When you launch the app, you should now see a small window with "Do a pushup!" which then opens a second window, with your 3D model. You can drag the handles to position and move the object.
Shadow #
Thanks to hhjgjhghjghjghjg for this suggestion to extend Entity
and introduce a enumerateHierarchy
method that can be used to walk through all the components of the usdz
source. The extension looks like this:
import RealityKit
extension Entity {
// Executes a closure for each of the entity's child and descendant
// entities, as well as for the entity itself.
//
// Set `stop` to true in the closure to abort further processing of the child entity subtree.
func enumerateHierarchy(_ body: (Entity, UnsafeMutablePointer<Bool>) -> Void) {
var stop = false
func enumerate(_ body: (Entity, UnsafeMutablePointer<Bool>) -> Void) {
guard !stop else {
return
}
body(self, &stop)
for child in children {
guard !stop else {
break
}
child.enumerateHierarchy(body)
}
}
enumerate(body)
}
}
It's then applied to the entity like so:
entity?.enumerateHierarchy { entity, stop in
if entity is ModelEntity {
entity.components.set(GroundingShadowComponent(castsShadow: true))
}
}
We should now have a pair of WindowGroups that load then the app loads, with one being a .plain
window which then triggers the appearance of the model
WindowGroup.
- Previous: Animated Blender entity in RealityView
- Next: Immersive spaces