Post

Replies

Boosts

Views

Activity

Loading USDZ files with SwiftUI
Hello! I've been trying to create a custom USDZ viewer using the Vision Pro. Basically, I want to be able to load in a file and have a custom control system I can use to transform, playback animations, etc. I'm getting stuck right at the starting line however. As far as I can tell, the only way to access the file system through SwiftUI is to use the DocumentGroup struct to bring up the view. This requires implementing a file type through the FileDocument protocol. All of the resources I'm finding use text files as their example, so I'm unsure of how to implement USDZ files. Here is the FileDocument I've built so far: import SwiftUI import UniformTypeIdentifiers import RealityKit struct CoreUsdzFile: FileDocument { // we only support .usdz files static var readableContentTypes = [UTType.usdz] // make empty by default var content: ModelEntity = .init() // initializer to create new, empty usdz files init(initialContent: ModelEntity = .init()){ content = initialContent } // import or read file init(configuration: ReadConfiguration) throws { if let data = configuration.file.regularFileContents { // convert file content to ModelEntity? content = ModelEntity.init(mesh: data) } else { throw CocoaError(.fileReadCorruptFile) } } // save file wrapper func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper { let data = Data(content) return FileWrapper(regularFileWithContents: data) } } My errors are on conversion of the file data into a ModelEntity and the reverse. I'm not sure if ModelEntity is the correct typing here, but as far as I can tell .usdz files are imported as ModelEntities. Any help is much appreciated! Dylan
4
0
2.2k
Mar ’24
Program runs fine in Vision Pro simulator but not on the actual device
I'm trying to figure out how to debug this issue. I have a fairly simple program that I built using the Hello World sample code as reference. Here is my code: import SwiftUI @main struct Core_USDZ_ViewerApp: App { var body: some Scene { // Main Menu scene // WindowGroup (id: "main-menu"){ CoreUsdzMenu() } // Scene that takes data as an input // for volumetric viewing // WindowGroup (for: URL.self) { $content in CoreUsdzVolume(url: content) } defaultValue: { URL(string: "https://developer.apple.com/augmented-reality/quick-look/models/pancakes/pancakes.usdz")! } .windowStyle(.volumetric) .defaultSize(width: 0.2, height: 0.3, depth: 0.3, in: .meters) // Full surround scene /* ImmersiveSpace { // put our immersive view here } */ } } In the simulator, this launches the main menu scene, but when installed on the Vision Pro using TestFlight it skips the main menu and goes straight to the second WindowGroup. Since the data isn't populated, it uses the defaultValue and just shows pancakes. I'm having trouble logging and debugging this issue, as I don't have access to the hardware myself. I have to push the code to TestFlight and wait for a coworker to test it. Does anyone have ideas of why this could be happening? Any help is appreciated. Logging and debugging tips especially. I'm used to just putting log messages in my code to debug, but maybe there are some breakpoint techniques I should be using here or something. Oh, also, here is my CoreUsdzMenu script: import SwiftUI import RealityKit import UniformTypeIdentifiers struct CoreUsdzMenu: View { @Environment(\.openWindow) private var openWindow @Environment(\.dismissWindow) private var dismissWindow @State var entity: Entity? = nil @State var showFilePicker: Bool = false init() { NSLog("In CoreUsdzMenu") } var body: some View { VStack{ Text("Core USDZ Viewer v1.1") .font(.title) .frame(width: 500.0, height: 100.0) /* usdz list HStack(spacing: 100.0) { Spacer() // Load the UsdzList view into the window! UsdzList() Spacer() }*/ /* button test for window Button { openWindow(value: usdzData[1].url) } label: { Text("open test window") }*/ // A view for displaying the loaded asset. /* RealityView( make: { content in // Add a placeholder entity to parent the entity to. // let placeholderEntity = Entity() placeholderEntity.name = "$__placeholder" if let loadedEntity = self.entity { placeholderEntity.addChild(loadedEntity) } content.add(placeholderEntity) }, update: { content in guard let placeholderEntity = content.entities.first(where: { $0.name == "$__placeholder" }) else { preconditionFailure("Unable to find placeholder entity") } // If there is a loaded entity, remove the old child, // and add the new one. // if let loadedEntity = self.entity { placeholderEntity.children.removeAll() placeholderEntity.addChild(loadedEntity) } } )*/ // A button that displays a file picker for loading a USDZ. // Button( action: { showFilePicker = true }, label: { Text("Load USDZ") } ) .padding() } // can import usdz and realityFile UTT types .fileImporter(isPresented: $showFilePicker, allowedContentTypes: [.usdz, .realityFile]) { result in // Get the URL of the USDZ picked by the user. // Guarded for errors. // guard let url = try? result.get() else { print("Unable to get URL") return } NSLog("In CoreUsdzMenu") // add new .usdz to our data list // for later!~ // use Observables n stuff //usdzData.addUsdz(url) // This task is just an asynchronous block of code. Not linked // to the RealityView explicitly; the update parameter of the // RealityView function responds when this task updates the entity. // Task { // As the app is sandboxed, permission needs to be // requested to access the file, as it's outside of // the sandbox. // if url.startAccessingSecurityScopedResource() { defer { url.stopAccessingSecurityScopedResource() } // Load the USDZ asynchronously. // On load, triggers RealityView's update. // //self.entity = try await Entity(contentsOf: url) // Try using the Volumetric Window to display the // content: // openWindow(value: url) } } } } } Thank you for reading!
2
0
577
Apr ’24