import OSLog import RealityKit import RealityKitContent import SwiftUI struct ThreeSixtySkysphereRealityView: View { let logger = Logger(subsystem: SUBSYSTEM, category: "ThreeSixtySkysphereRealityView") @Environment(\.scenePhase) var scenePhase @Environment(AppModel.self) var appModel let rootEntity = Entity() let threeSixtySkysphereEntity = Entity() var body: some View { RealityView { content, attachments in logger.info("\(#function) \(#line) `RealityView { content, attachments in`") await loadScenes(content: content, attachments: attachments) } update: { content, attachments in logger.info("\(#function) \(#line) `update: { content, attachments in`") } placeholder: { let _ = logger.info("\(#function) \(#line) `placeholder`") ProgressView() } attachments: { let _ = logger.info("\(#function) \(#line) `attachments`") Attachment(id: "AttachmentID.exitThreeSixty") { Button(action: { logger.info("\(#function) \(#line) button pressed") appModel.showingScene = .intro }) { Text("Close") } .glassBackgroundEffect() .cornerRadius(25) } } } func loadScenes(content: RealityViewContent, attachments: RealityViewAttachments) async { logger.info("\(#function) \(#line)") addSkyboxImage(entity: threeSixtySkysphereEntity) rootEntity.addChild(threeSixtySkysphereEntity) guard let exitThreeSixtyAttachement = attachments.entity(for: "AttachmentID.exitThreeSixty") else { logger.error("\(#function) \(#line) missing attachment") assertionFailure("missing attachment") return } exitThreeSixtyAttachement.scale = scaleForAttachements let anchorEntity = AnchorEntity(world: [0, 0.6, -1.8]) anchorEntity.addChild(exitThreeSixtyAttachement) rootEntity.addChild(anchorEntity) // exitThreeSixtyAttachement.isEnabled = false content.add(rootEntity) // This delay in displaying the attachment resolves an issue whereby there would // otherwise be a slight flicker and dimming upon initial load of the scene. // DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(500)) { // exitThreeSixtyAttachement.isEnabled = true // } } func addSkyboxImage(entity: Entity) { do { let uiImage = UIImage(named: "Skydome_8k")! // There is a hard limit of 8,192px width images in this `TextureResource.generate` method // If we try to generate a texture from an image of 8,193px width, texture generation will fail. let texture = try TextureResource(image: uiImage.cgImage!, options: TextureResource.CreateOptions.init(semantic: nil)) var material = UnlitMaterial() material.color = .init(texture: .init(texture)) entity.components.set(ModelComponent( mesh: .generateSphere(radius: 1000), materials: [material]) ) entity.scale *= .init(x: -1, y: 1, z: 1) entity.transform.translation += SIMD3(0.0, 1.0, 0.0) entity.transform.rotation = simd_quatf(angle: 89.5, axis: [0, 1, 0]) // rotate so that user faces the sea } catch (let error) { logger.error("\(#function) \(#line) texture not loaded with error \(error.localizedDescription)") assertionFailure("texture not loaded with error \(error.localizedDescription)") } } } #Preview { ThreeSixtySkysphereRealityView() }