7
votes

I try to load dynamically .scn and texture files from server URL in ARKit application. I have achieved loading .scn file from web url this way.. But after running I am not seeing any textures on device. I get following error message.

ARKitExample[3016:995067] [SceneKit] Error: Failed loading : <C3DImage 0x1d42e1980 src:file:///var/containers/Bundle/Application/622ACF79-2318-4953-A9AE-9A7F2B453AFB/ARKitExample.app/textures/lamp_DIFFUSE.png [0.000000x0.000000]>
ARKitExample[3016:995067] [SceneKit] Error: Failed loading : <C3DImage 0x1d00fe800 src:file:///var/containers/Bundle/Application/622ACF79-2318-4953-A9AE-9A7F2B453AFB/ARKitExample.app/textures/lamp_NORMAL.png [0.000000x0.000000]>
ARKitExample[3016:995067] [SceneKit] Error: Failed loading : <C3DImage 0x1d40ff800 src:file:///var/containers/Bundle/Application/622ACF79-2318-4953-A9AE-9A7F2B453AFB/ARKitExample.app/textures/lamp_METALLIC.png [0.000000x0.000000]>
ARKitExample[3016:995067] [SceneKit] Error: Failed loading : <C3DImage 0x1d42e0d80 src:file:///var/containers/Bundle/Application/622ACF79-2318-4953-A9AE-9A7F2B453AFB/ARKitExample.app/textures/lamp_ROUGHNESS.png [0.000000x0.000000]>
ARKitExample[3016:995067] [SceneKit] Error: Failed loading : <C3DImage 0x1d02e2e00 src:file:///var/containers/Bundle/Application/622ACF79-2318-4953-A9AE-9A7F2B453AFB/ARKitExample.app/textures/lamp_SHADOW.png [0.000000x0.000000]>

how to resolve this issues. Thank you

@ Xartec I have tried your mentioned way but I did not get any response.How can I fix it?

let urlString = "\("https://d533c2fd.ngrok.io/")\("mode")"
        let url = URL.init(string: urlString)
        let request = URLRequest(url: url!)
        let session = URLSession.shared
        let downloadTask = session.downloadTask(with: request,
                completionHandler: { (location:URL?, response:URLResponse?, error:Error?)
                -> Void in
                print("location:\(String(describing: location))")
                let locationPath = location!.path
                  //  let documents:String = NSHomeDirectory() + "/Documents/"+"\(self.modelName).\(self.fileExtension)"
                   // let documents:String = NSHomeDirectory() + "/Documents/"+"\("model5").\("scn")"
                    let documents:String = NSHomeDirectory() + "/Documents/"+"\("mode")"

                ls = NSHomeDirectory() + "/Documents"
                let fileManager = FileManager.default
                if (fileManager.fileExists(atPath: documents)){
                     try! fileManager.removeItem(atPath: documents)
                }
                try! fileManager.moveItem(atPath: locationPath, toPath: documents)
                print("new location:\(documents)")
                let node = SCNNode()

                    do {
                        let documentss = documents + "\("model5").\("scn")"
                        let scene = try SCNScene(url: URL(fileURLWithPath: documentss), options: [.overrideAssetURLs: true])
                        let nodess = scene.rootNode.childNode(withName: "SketchUp", recursively: true)
                        node.addChildNode(nodess!)
                        self.addChildNode(node)
                        self.modelLoaded = true

                    } catch {}

        })
        downloadTask.resume()
1
@Xartec It is a duplicate... However, that earlier questioner didn't find a solution to the problem of textures not working to his dynamically loaded scn file from a remote URL. Any ideas? - Clay
The solution is described in the first and only answer that question has, posted a week after the OP said he didn’t find a solution: “You should download the file along with its textures, and then load the scene. Note that the .scn file and the textures should be in the same directory unless you want to add some loading options.” - Xartec
@ Xartec i have updated my question please check. - Raj
I handle this downloading a zip that contains both the 3D model (in my case it's a mtl/obj) and all needed textures. I'll post relevant code when back home - leandrodemarco

1 Answers

7
votes

Here's the relevant code. I use Alamofire to download from the server and ZIPFoundation for unzipping. In my case I work with mtl/obj files, but working with scn or dae, should work just fine too.

let modelsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]

func loadNodeWithID(_ id: String, completion: @escaping (SCNNode?) -> Void) {
    // Check that assets for that model are not already downloaded
    let fileManager = FileManager.default
    let dirForModel = modelsDirectory.appendingPathComponent(id)
    let dirExists = fileManager.fileExists(atPath: dirForModel.path)
    if dirExists {
        completion(loadNodeWithIdFromDisk(id))
    } else {
        let dumbURL = "http://yourserver/yourfile.zip"
        downloadZip(from: dumbURL, at: id) {
            if let url = $0 {
                print("Downloaded and unzipped at: \(url.absoluteString)")
                completion(self.loadNodeWithIdFromDisk(id))
            } else {
                print("Something went wrong!")
                completion(nil)
            }
        }
    }
}

func loadNodeWithIdFromDisk(_ id: String) -> SCNNode? {
    let fileManager = FileManager.default
    let dirForModel = modelsDirectory.appendingPathComponent(id) 
    do {
        let files = try fileManager.contentsOfDirectory(atPath: dirForModel.path)
        if let objFile = files.first(where: { $0.hasSuffix(".obj") }) {
            let objScene = try? SCNScene(url: dirForModel.appendingPathComponent(objFile), options: nil)
            let objNode = objScene?.rootNode.firstChild()
            return objNode
        } else {
            print("No obj file in directory: \(dirForModel.path)")
            return nil
        }
    } catch {
        print("Could not enumarate files or load scene: \(error)")
        return nil
    }
}

func downloadZip(from urlString: String, at destFileName: String, completion: ((URL?) -> Void)?) {
    print("Downloading \(urlString)")
    let fullDestName = destFileName + ".zip"

    let destination: DownloadRequest.DownloadFileDestination = { _, _ in
        let fileURL = modelsDirectory.appendingPathComponent(fullDestName)
        return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
    }

    Alamofire.download(urlString, to: destination).response { response in
        let error = response.error
        if error == nil {
            if let filePath = response.destinationURL?.path {
                let nStr = NSString(string: filePath)
                let id = NSString(string: nStr.lastPathComponent).deletingPathExtension
                print(response)
                print("file downloaded at: \(filePath)")
                let fileManager = FileManager()
                let sourceURL = URL(fileURLWithPath: filePath)
                var destinationURL = modelsDirectory
                destinationURL.appendPathComponent(id)
                do {
                    try fileManager.createDirectory(at: destinationURL, withIntermediateDirectories: true, attributes: nil)
                    try fileManager.unzipItem(at: sourceURL, to: destinationURL)
                    completion?(destinationURL)
                } catch {
                    completion?(nil)
                    print("Extraction of ZIP archive failed with error: \(error)")
                }
            } else {
                completion?(nil)
                print("File path not found")
            }
        } else {
            // Handle error
            completion?(nil)
        }
    }
}