7

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()
Raj
  • 485
  • 9
  • 18
  • Possible duplicate of [Importing .scn file to scene from URL with textures](https://stackoverflow.com/questions/47677954/importing-scn-file-to-scene-from-url-with-textures) – Xartec Feb 06 '18 at 16:49
  • @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 Feb 06 '18 at 23:51
  • 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 Feb 07 '18 at 00:30
  • @ Xartec i have updated my question please check. – Raj Feb 07 '18 at 04:49
  • 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 Feb 07 '18 at 17:43
  • @ leandrodemarco thanks for your replay.. if get chance pls post your sample code.. Thanks – Raj Feb 08 '18 at 04:12
  • @Raj, I wanted a solution that only used swift built-in networking capabilities, like your attempt above . I think wants wrong with your code above is you need to configure your webserver to autoindex the directory with the scnfiles and texture subdirectories. When you fetch the directory url, you get the index html page with href links you can then html parse to array of urls. From there you can copy the models to the documents directory and textures to subdirectory. I got it to working in the playgrounds app on my ipad, however, I need to tidy up the code before posting. – Clay Feb 12 '18 at 02:39

1 Answers1

7

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)
        }
    }
}
leandrodemarco
  • 1,552
  • 1
  • 15
  • 22
  • @leandrodemarcoi will check and update shortly.. Thanks – Raj Feb 09 '18 at 04:44
  • how can i call this method func loadNodeWithID(_ id: String, completion: @escaping (SCNNode?) -> Void) { – Raj Feb 09 '18 at 05:27
  • 1
    i have tried your code but still not loading textures objFile===== model.scn objFile===== file:///var/mobile/Containers/Data/Application/455027C2-41EB-4FD5-97CF-59156B9469C7/Documents/House/model.scn 2018-02-09 12:22:40.760478+0530 ARKitExample[5654:2247542] [SceneKit] Error: Failed loading : – Raj Feb 09 '18 at 06:56
  • Hmm... any chance you can share the URL? – leandrodemarco Feb 09 '18 at 11:46
  • 1
    Thanks for your reply.. this is my server url for models and textures https://ac5ee9f8.ngrok.io/House.zip. Thanks – Raj Feb 12 '18 at 03:51
  • 1
    Hey @Raj does this code work fine for you? Did you have to change `$0.hasSuffix(".obj")` into something else (e.g. "scn")? – Cesare Apr 30 '19 at 08:31
  • Also it would be great if you could share a sample URL. – Cesare Apr 30 '19 at 08:32