Dynamically load Collada files in SceneKit at runtime

The problem

For an upcoming project, a client asked me if I could build a prototype which could load Collada files at runtime.  The flow has to be like this

  • User downloads Collada zip file while using the app (e.g. in-app purchase)
  • Collada file gets unzipped
  • Show the downloaded Collada file in the app

I started looking a possible 3D engines which I could use like Unity, but then I remembered Apple has released the SceneKit SDK which allows pretty high-level access, but with excellent performance.

I looked at the documentation and at first it looked quite simple to do.  Sadly Apple does some magic behind the scenes with the exported Collada file to compress and optimize it.

If you try to use a downloaded .dae file and load it at runtime, you’ll get the following error.

scenekit COLLADA files are not supported on this platform.

Luckily I could find out which tools are used and now it seems possible to do.

You can find the complete project with all the files and converter scripts on Github.

A solution

So we go with SceneKit.

Create a new Xcode project, choose  the Game template. Choose for SceneKit as Game Technology.

First you’ll need to create a Collada file (.dae) with a 3D program, like Blender (free), Maya, etc … (In the git repository you can find a test file).  You can drop this file in the art.scnassets directory and you are done … except I want to load this file at runtime, so it is not accessible at compile-time.  That’s a totally different story, as Apple applies some magic to the scnassets directory to optimize the included Collada files.

So how can we fixed this? I looked at the build logs what exactly happens to the scnassets folder to figure out what kind of magic is happening.  It seems Apple calls a script named copySceneKitAssets, this script calls eventually scntool  to optimize the included .dae files.

Alright with this knowledge I extracted these two scripts out of their directory (/Applications/Xcode/Contents/Developer/usr/bin) and placed them somewhere on my system.

So every time my client creates a new 3D model that should be available for sale via IAP, he should do the following.

  • Create a product.scnassets folder
  • Run the script

  • Zip the result
  • Put it on the server

Now we can download the zip file with the following code in our GameViewController.m (I use AFNetworking and SSZipArchive to do the heavy lifting).

In viewDidLoad: I added the following code, it just downloads the zip file from our server and unzips it.

When everything is unzipped we need to create our scene and add the downloaded asset. To do this we need the help of the SCNSceneSource class. Luckily it’s really easy to accomplish this. Just get the path to our scnassets folder and feed it to the initializer.

After this we can just get a reference to the Cube node in our Collada file with the following line of code.  The identifier is the name you gave the 3D object in your 3D program. In my case it was ‘Cube’.

Last bit is create an empty scene, set up a camera and add a light.

Final step … add the cube to the scene with this line of code.

And lets put the scene in a SCNView object, so we can show it.

That’s it … run the code and you will see that

  1. The Collada zip file gets downloaded from our server
  2. The file gets unzipped
  3. The resulting Collada file gets loaded into memory
  4. The Collada file gets shown!