How can I show a panorama photo as is done in the Photos app for Vision Pro?
Panorama Photo
If anyone runs into this, please file an enhancement request at https://feedbackassistant.apple.com. There is no high-level panoramic API to do this.
Some steps you can take to do this yourself are as follows:
- Create (and then load) a "pano mesh"
- Load the pano as a TextureResource
- Put the texture in an UnlitMaterial
- Use the material with the pano mesh
- Scale the pano mesh based on the aspect ratio of the texture
In code, this looks like:
RealityView { content in
do {
// Load the scene that has the pano mesh.
let scene = try await Entity(named: "PanoMesh", in: realityKitContentBundle)
// Find the entity that has the model component holding the pano mesh.
let panoEntity = scene.findEntity(with: ModelComponent.self)!
// Get the model component.
var panoModel = panoEntity.components[ModelComponent.self]!
// Create a new UnlitMaterial (you most likely don't want to apply lighting to a pano image).
var material = UnlitMaterial()
// Load the pano texture from the main bundle.
let texture = try! await TextureResource(named: "MyPano")
// Set the color of the unlit material to the pano image (tint white to make it render normally).
material.color = .init(tint: .white, texture: .init(texture))
// Set the materials of the model component with the new material.
panoModel.materials = [material]
// Set the updated model component on the entity.
panoEntity.components.set(panoModel)
// Calculate the aspect ratio of the pano.
let aspectRatio = Float(texture.width) / Float(texture.height)
// Scale the scene according to the aspect ratio of the pano.
scene.scale = [aspectRatio, 1, aspectRatio]
// This is usually a good position for content when an ImmersiveScene launches.
scene.position = [0, 1.3, -1.3]
// This constraint restricts the mesh from appearing greater than 3 meters wide / deep.
if aspectRatio > 3 {
scene.scale *= 3 / aspectRatio
}
// Add the scene to the view's content.
content.add(scene)
} catch {
fatalError(error.localizedDescription)
}
}
extension Entity {
func findEntity(where predicate: (Entity) -> Bool) -> Entity? {
var stack = [self]
while let next = stack.popLast() {
if predicate(next) {
return next
} else {
stack.append(contentsOf: next.children)
}
}
return nil
}
func findEntity<C: Component>(with componentType: C.Type) -> Entity? {
findEntity { $0.components.has(componentType) }
}
}