4
votes

According to WWDC 2013 What's new in SceneKit target geometries for morphers can be contained in a .dae file:

All the morphing information and animations can be loaded from a DAE file or you can create everything programmatically."

I have a collada .dae file that contains morph controllers. The file was created by Blender's Collada exporter, with "Include Shape Keys" selected. This converts Blender's shape keys into Collada's morph controllers. When I inspect the resulting file, I can see the shape keys listed after the main geometry in <library_geometries>. The headers look like this:

<geometry id="Octopus-mesh_morph_Rest" name="Rest">
  <mesh>
    <source id="Octopus-mesh_morph_Rest-positions">

where Rest is the name of the shape key. The geometry for the shape key/ morph target then follows this header.

After all the geometry is defined, there's the <library_controllers>:

<library_controllers>
<controller id="Octopus-morph" name="Octopus-morph">
  <morph source="#Octopus-mesh" method="NORMALIZED">
    <source id="Octopus-targets">
      <IDREF_array id="Octopus-targets-array" count="1">Octopus-mesh_morph_Rest</IDREF_array>
      <technique_common>
        <accessor source="#Octopus-targets-array" count="1" stride="1">
          <param name="IDREF" type="IDREF"/>
        </accessor>
      </technique_common>
    </source>
    <source id="Octopus-weights">
      <float_array id="Octopus-weights-array" count="1">0</float_array>
      <technique_common>
        <accessor source="#Octopus-weights-array" count="1" stride="1">
          <param name="MORPH_WEIGHT" type="float"/>
        </accessor>
      </technique_common>
    </source>
    <targets>
      <input semantic="MORPH_TARGET" source="#Octopus-targets"/>
      <input semantic="MORPH_WEIGHT" source="#Octopus-weights"/>
    </targets>
  </morph>
</controller>
</library_controllers>

How can I access these morph controllers from within SceneKit? In the Xcode Scene Editor, there's no indication that the morph controllers have been recognised, eg pressing play does nothing, the animations folder is empty, and there's no additional geometries besides the base geometry.

When I load the node, it has no morpher property attached, and no animation keys. I've tried searching the tree for entries named "Rest" but nothing is there.

    let collada = SCNScene(named: "octopus.dae", inDirectory: "art.scnassets", options: nil)
    let octopus = collada?.rootNode.childNodeWithName("Octopus", recursively: true)
    octopus?.position = SCNVector3(0,-2,0)
    print(octopus?.morpher?.animationKeys) //nil
    print(octopus?.animationKeys) //Optional([])

Has anyone managed to load SCNMorpher targets from the morph controllers of a Collada .dae file? Could it be an issue with the Blender .dae exporter? Objective-C answers as well as Swift welcome. Thanks.

EDIT

I also tried using NSBundle to load the .dae, and placing the .dae both inside and outside the art.scnassets folder, to see if it makes a difference, but still no dice:

    let collada = NSBundle.mainBundle().URLForResource("octopus", withExtension: "dae")!
    let sceneSource = SCNSceneSource(URL: collada, options: [
        SCNSceneSourceAnimationImportPolicyKey : SCNSceneSourceAnimationImportPolicyDoNotPlay
        ])
    let octopusDae = sceneSource?.entryWithIdentifier("Octopus", withClass: SCNNode.self)
    //let deform = sceneSource?.entryWithIdentifier("Rest", withClass: SCNGeometry.self)
            print(sceneSource?.entriesPassingTest(){(entry, id, stop) in return id.containsString("Rest") })
    print(octopusDae!.morpher)
    print(octopusDae!.animationKeys)

EDIT 2

I have managed to get this working with .obj files, by saving each shape-key as a separate .obj file. But I would really like to get this working with Blender's .dae files as it would make iterative development and working with models with lots of shape-keys that much easier (plus, there are some strange quirks to working with Blender .obj files in SceneKit, eg all the texture coordinates seem to be flipped vertically). All the geometry I need is in the .dae file. I just can't get SceneKit to see it.

1

1 Answers

3
votes

Blender isn't writing instance_controllers under the nodes that need them. See Blender issue: T50807.

If you just want to load SCNMorphers from shape keys, you can replace the instance_geometry elements under affected nodes with an instance_controller that refers to the appropriate entry under library_controllers.

<node id="Cube" name="Cube" type="NODE">
    <matrix sid="transform">1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1</matrix>
    <instance_geometry url="#Cube-mesh" name="Cube">
      <bind_material>
        <technique_common>
          <instance_material symbol="Material-material" target="#Material-material"/>
        </technique_common>
      </bind_material>
    </instance_geometry>
  </node>

Becomes:

  <node id="Cube" name="Cube" type="NODE">
    <matrix sid="transform">1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1</matrix>
    <instance_controller url="#Cube-morph" name="Cube-morph">
      <bind_material>
        <technique_common>
          <instance_material symbol="Material-material" target="#Material-material"/>
        </technique_common>
      </bind_material>
    </instance_controller>
  </node>

SceneKit has its own issues. As far as I know, it won't properly import a Collada file with a node that has both an SCNSkinner (aka Blender armature) and an SCNMorhper (aka Blender shape key). Blender won't export this, but you can see the problem by using the Collada CTS test files. Morph_on_skin, skin_on_morph, morph_on_morph all have issues in SceneKit.

I made a tool that fixes the simple case of missing SCNMorphers. It can be found here: https://github.com/JonAllee/ColladaMorphAdjuster