是有办法将一个GLB文件下载到一个ArKit/RealityKit应用程序中吗?

huangapple go评论78阅读模式
英文:

is there a way to download a GLB file into a ArKit/RealityKit application?

问题

以下是您提供的代码的翻译部分:

我想要一个能够处理3D .glb文件URL的应用程序/模块,将其下载到文件系统,最后加载为实体并锚定的应用程序/模块。

我认为我的大部分代码都已完成,不幸的是,我遇到了.glb和.gltf文件的问题,而.usdz文件则按预期加载

我将相同的.glb文件转换为.usdz后添加到Experience,我可以从中加载它并在我的应用程序中执行一些操作。

但我的主要目标是能够为opensea存储的.glb模型提供相同的操作。换句话说,从Experience加载正常,但从URL加载同一.glb文件则不正常

1) 是否有加载.glb的方法,我可能遗漏了什么
2) 应用程序中是否可以将.glb转换为.usdz(我只找到了一个命令行工具)

让我分享我的代码:
class RealityViewController: UIViewController {
    
    var arView: ARView = ARView(frame: .zero)
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 设置ARView的会话配置以进行LiDAR扫描
        let config = ARWorldTrackingConfiguration()
        if ARWorldTrackingConfiguration.supportsSceneReconstruction(.mesh) {
            config.sceneReconstruction = .mesh
        }
        
        // 启用水平和垂直平面检测
        config.planeDetection = [.horizontal, .vertical]
        
        // 启用自动环境纹理
        config.environmentTexturing = .automatic
        arView.environment.sceneUnderstanding.options = []
        arView.environment.sceneUnderstanding.options.insert(.receivesLighting)
        arView.environment.sceneUnderstanding.options.insert(.occlusion)
        arView.environment.sceneUnderstanding.options.insert(.collision)
        arView.environment.sceneUnderstanding.options.insert(.physics)
        
        arView.renderOptions = [.disableFaceMesh, .disableHDR, .disableMotionBlur, .disableCameraGrain, .disableDepthOfField]
        
        // 使用配置运行会话
        arView.session.run(config)
        
        // 将ARView的框架设置为覆盖整个屏幕
        arView.frame = view.bounds
        
        // 将ARView添加到视图层次结构中
        view.addSubview(arView)
        
        // 确保ARView的autoresizingMask已设置,以使其随其父视图调整大小
        arView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    }
    
    func loadModel(from url: URL) {
        print("检查位于 \(url.absoluteString) 的文件")
        
        if FileManager.default.fileExists(atPath: url.path) {
            print("文件存在,加载模型")
            DispatchQueue.main.async {
                do {
                    let entity = try Entity.load(contentsOf: URL(string: url.absoluteString)!)
                    if let anchorEntity = entity as? AnchorEntity {
                        DispatchQueue.main.async {
                            self.arView.scene.addAnchor(anchorEntity)
                            print("锚定 AnchorEntity")
                        }
                    } else {
                        let anchor = AnchorEntity()
                        anchor.addChild(entity)
                        DispatchQueue.main.async {
                            self.arView.scene.addAnchor(anchor)
                            print("锚定 ModelEntity")
                        }
                    }
                    print("加载完成")
                } catch {
                    print("无法加载模型: \(error)")
                }
            }
        } else {
            print("文件不存在,GLB下载失败")
        }
    }
}

import SwiftUI

struct RealityKitView: UIViewControllerRepresentable {
    
    let url: URL
    var realityViewController = RealityViewController()
    
    func makeUIViewController(context: Context) -> RealityViewController {
        print("makeUIViewController - realityViewController")
        return realityViewController
    }
    
    func updateUIViewController(_ uiViewController: RealityViewController, context: Context) {}
    
    func loadModel() {
        print("loadModel")
        GLBManager.shared.downloadAndStoreGLB(from: url) { savedURL in
            if let savedURL = savedURL {
                DispatchQueue.main.async {
                    self.realityViewController.loadModel(from: savedURL)
                }
            }
        }
    }
}

import Foundation

class GLBManager {
    
    static let shared = GLBManager()
    
    func downloadAndStoreGLB(from url: URL, completion: @escaping (URL?) -> Void) {
        
        let task = URLSession.shared.downloadTask(with: url) { localURL, urlResponse, error in
            
            guard let localURL = localURL else {
                completion(nil)
                return
            }
            
            do {
                let fileName = url.lastPathComponent
                let cleanedFileName = fileName.replacingOccurrences(of: " ", with: "_")
                let applicationSupportDirectory = try FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
                let savedURL = applicationSupportDirectory.appendingPathComponent(cleanedFileName)
                
                try FileManager.default.moveItem(at: localURL, to: savedURL)
                completion(savedURL)
            } catch {
                print("文件错误: \(error)")
                completion(nil)
            }
        }
        
        task.resume()
    }
}

struct RealityContentView: View {
    var body: some View {
        if let url = URL(string: "https://developer.apple.com/augmented-reality/quick-look/models/gramophone/gramophone.usdz") {
            let realityKitView = RealityKitView(url: url)
            realityKitView
                .onAppear {
                    realityKitView.loadModel()
                }
                .edgesIgnoringSafeArea(.all)
        }
    }
}

希望这能帮助您理解代码的翻译。如果您需要更多帮助,请随时提出问题。

英文:

I would like to have an application/module which can handle an URL of a 3D .glb file, download it to the file-system, and finally loads it as Entity and anchors it.

I think I have most of the code done, unfortunately I am facing issues with both .glb and .gltf files, the .usdz files loads as expected.

I have the same .glb file converted to .usdz added to Experience and I can load it from there in my application and perform some actions on it.

But my main aim is to be able to provide these same actions for the on opensea store .glb models also. In other words loading from Experience works, loading from url not for the same .glb file.

  1. is there a way to load .glb and I am just missing something
  2. can the .glb converted in application (I have found just a command line tool) to the .usdz ?

let me share my code:

import UIKit
import ARKit
import RealityKit
import Combine

class RealityViewController: UIViewController {
    
    var arView: ARView = ARView(frame: .zero)
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        
        // Set up the ARView's session configuration for LiDAR scanning
        let config = ARWorldTrackingConfiguration()
        if ARWorldTrackingConfiguration.supportsSceneReconstruction(.mesh) {
            config.sceneReconstruction = .mesh
        }
        
        // Enable horizontal and vertical plane detection
        config.planeDetection = [.horizontal, .vertical]
        
        // Enable automatic environment texturing
        config.environmentTexturing = .automatic
        arView.environment.sceneUnderstanding.options = []
        arView.environment.sceneUnderstanding.options.insert(.receivesLighting)
        arView.environment.sceneUnderstanding.options.insert(.occlusion)
        arView.environment.sceneUnderstanding.options.insert(.collision)
        arView.environment.sceneUnderstanding.options.insert(.physics)
        
        arView.renderOptions = [.disableFaceMesh, .disableHDR, .disableMotionBlur, .disableCameraGrain, .disableDepthOfField]
        
        // Run the session with the configuration
        arView.session.run(config)
        
        // Set the frame of your ARView to cover the entire screen
        arView.frame = view.bounds
        
        // Add your ARView to the view hierarchy
        view.addSubview(arView)
        
        // Make sure ARView's autoresizingMask is set so it resizes with its parent view
        arView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    }
    
    
    func loadModel(from url: URL) {
        
        print("Checking file at \(url.absoluteString)")
        
        if FileManager.default.fileExists(atPath: url.path) {
            print("File exists, loading model")
            DispatchQueue.main.async {
                do {
                    let entity = try Entity.load(contentsOf: URL(string: url.absoluteString)!)
                    if let anchorEntity = entity as? AnchorEntity {
                        DispatchQueue.main.async {
                            self.arView.scene.addAnchor(anchorEntity)
                            print("AnchorEntity anchored")
                        }
                    }else{
                        let anchor = AnchorEntity()
                        anchor.addChild(entity)
                        DispatchQueue.main.async {
                            self.arView.scene.addAnchor(anchor)
                            print("ModelEntity anchored")
                        }
                    }
                    print("Finished loading")
                } catch {
                    print("Unable to load model: \(error)")
                }
            }
        } else {
            print("File does not exist, GLB download have failed")
        }
        
    }
}

import SwiftUI

struct RealityKitView: UIViewControllerRepresentable {
    
    let url: URL
    var realityViewController = RealityViewController()
    
    func makeUIViewController(context: Context) -> RealityViewController {
        
        print("makeUIViewController - realityViewController")
        return realityViewController
    }
    
    func updateUIViewController(_ uiViewController: RealityViewController, context: Context) {}
    
    func loadModel() {
        print("loadModel")
        GLBManager.shared.downloadAndStoreGLB(from: url) { savedURL in
            if let savedURL = savedURL {
                DispatchQueue.main.async {
                    self.realityViewController.loadModel(from: savedURL)
                }
            }
        }
    }
}

import Foundation

class GLBManager {
    
    static let shared = GLBManager()
    
    func downloadAndStoreGLB(from url: URL, completion: @escaping (URL?) -> Void) {
        
        let task = URLSession.shared.downloadTask(with: url) { localURL, urlResponse, error in
            
            guard let localURL = localURL else {
                completion(nil)
                return
            }
            
            do {
                let fileName = url.lastPathComponent
                let cleanedFileName = fileName.replacingOccurrences(of: " ", with: "_")
                let applicationSupportDirectory = try FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
                let savedURL = applicationSupportDirectory.appendingPathComponent(cleanedFileName)
            
                try FileManager.default.moveItem(at: localURL, to: savedURL)
                completion(savedURL)
            } catch {
                print("File error: \(error)")
                completion(nil)
            }
        }
        
        task.resume()
    }
}

//https://openseauserdata.com/files/1e23b556e07fb653bb538f8ffc2ba42d.glb
//https://openseauserdata.com/files/b1e1e80a7a2006598b20e3f01097c359.glb
//https://openseauserdata.com/files/f886842eb950813ba2f7ba775de327fc.glb

//WHEELS
//https://openseauserdata.com/files/afb49aede036bcb4f37f5785b88b9732.glb

//sample
//https://github.com/KhronosGroup/glTF-Sample-Models/blob/master/2.0/2CylinderEngine/glTF-Binary/2CylinderEngine.glb
//https://github.com/KhronosGroup/glTF-Sample-Models/blob/master/1.0/Avocado/glTF-Embedded/Avocado.gltf
//https://developer.apple.com/ar/photogrammetry/AirForce.usdz

struct RealityContentView: View {
    var body: some View {
        if let url = URL(string: "https://developer.apple.com/augmented-reality/quick-look/models/gramophone/gramophone.usdz") {
            let realityKitView = RealityKitView(url: url)
            realityKitView
                .onAppear {
                    realityKitView.loadModel()
                }
                .edgesIgnoringSafeArea(.all)
        }
    }
}

答案1

得分: 1

在经过一周的搜索后,我发现这种方法的最佳解决方案是一个单独的后端层,可以存储USDZ文件,并在事先或使用一些惰性加载机制进行准备。

  • 您需要一个返回USDZ文件的端点。
  • 后端定期作业检查GLB集合,如果有新的GLB文件,就会下载并在后端处理。

我将使用这个GitHub仓库:https://github.com/google/usd_from_gltf

英文:

after a week of searching I have found out that the best solution for this approach would be a separate backend layer, which can store the USDZ, and prepare it beforehand, or with some lazy loading mechanism.

  • you need an endpoint which returns you USDZ
  • backend periodic job which checks the GLB collection and if there is a new one it downloads it and processes on the backend side.

I would use this github repo for it

答案2

得分: 1

你可以下载glb/gltf并使用此转换器进行转换,然后在基于RealityKit的应用中加载,而不是创建后端层。

英文:

Instead of creating the backend layer, you could also download the glb/gltf and convert using this converter and load in RealityKit based app.

huangapple
  • 本文由 发表于 2023年6月5日 00:17:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/76401338.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定