iosscenekitmorphing

Scene Kit SCNMorpher not working with primitives like SCNSphere etc


Has anybody got the SCNMorpher to work? If so, what exactly have you done?

I'm down now to a small test program, that should show a red cone and when I tap on the screen it should (should!) morphe that into a sphere/ball. But the only thing that happens is, that the first geometry goes away (get very small) and the second one (Morphers first target) never appears.

I must miss something very simple. Can anybody get me on the horse, please?

import SceneKit
private let DISTANCE_FAKTOR = CGFloat(sqrt(3.0)/2.0)

class GameViewController: UIViewController {
override func viewDidLoad() {
    super.viewDidLoad()

    let (scene, view) = configScene(self.view.frame)
    self.view = view

    // Position camera to see picture full screen and light at same position
    let pov = SCNVector3(x: 0.0, y: 0.0, z: Float(max(view.frame.width, view.frame.height) * DISTANCE_FAKTOR))
    let pol = SCNVector3(x: 0.0, y: 0.0, z: Float(max(view.frame.width, view.frame.height) * DISTANCE_FAKTOR))

    scene.rootNode.addChildNode(makeShapes()) // Create and add background plane
    scene.rootNode.addChildNode(makeCamera(pov)) // Add camera to scene
    scene.rootNode.addChildNode(makeLight(pol)) // Add light to scene

    // add a tap gesture recognizer
    view.gestureRecognizers = [UITapGestureRecognizer(target: self, action: "handleTap:")]
}

func handleTap(gestureRecognize: UIGestureRecognizer) {
    NSLog("Tapped")
    if let effectNode = (view as? SCNView)?.scene?.rootNode.childNodeWithName("EFFECT", recursively: true) {
        NSLog("Animating")
        animate(effectNode)
    }
}

func makeShapes() -> SCNNode {
    // First shape is a cone
    let torus = SCNCone(topRadius: 0.0, bottomRadius: view.frame.width/2.0, height: view.frame.width/4.0)
    torus.firstMaterial = SCNMaterial()
    torus.firstMaterial?.diffuse.contents = UIColor.redColor()

    // Now an additional sphere/ball
    let sphere = SCNSphere(radius: view.frame.width/2.0)
    sphere.firstMaterial = SCNMaterial()
    sphere.firstMaterial?.diffuse.contents = UIColor.greenColor()

    // Put all in the node
    let node = SCNNode()
    node.geometry = torus
    node.morpher = SCNMorpher()
    node.morpher?.targets = [sphere]
    node.name = "EFFECT"

    // I would expect now something between a ball and a torus in red/green
    node.morpher?.setWeight(0.5, forTargetAtIndex: 0)
    return node
}

func animate(node: SCNNode) {
    SCNTransaction.begin()
    SCNTransaction.setAnimationDuration(5.0)
    node.morpher?.setWeight(1.0, forTargetAtIndex: 0) // From torus to ball

    SCNTransaction.setCompletionBlock {
        NSLog("Transaction completing")
        SCNTransaction.begin()
        SCNTransaction.setAnimationDuration(2.5)
        node.morpher?.setWeight(0.0, forTargetAtIndex: 0) // And back
        SCNTransaction.commit()
    }
    SCNTransaction.commit()
}

func configScene(frame: CGRect) -> (scene: SCNScene, view: SCNView) {
    let view = SCNView()
    view.frame = frame
    view.autoresizingMask = UIViewAutoresizing.allZeros
    view.backgroundColor = UIColor.blueColor()

    let scene = SCNScene()
    view.scene = scene

    return (scene, view)
}

func makeCamera(pov: SCNVector3) -> SCNNode {
    let camera = SCNCamera()
    camera.zFar = Double(pov.z)

    let node = SCNNode()
    node.position = pov
    node.camera = camera

    return node
}

func makeLight(pol: SCNVector3) -> SCNNode {
    let light = SCNLight()
    light.type = SCNLightTypeOmni
    light.zFar = CGFloat(pol.z)

    let node = SCNNode()
    node.position = pol
    node.light = light

    return node
}

override func shouldAutorotate() -> Bool {
    return true
}

override func prefersStatusBarHidden() -> Bool {
    return true
}

override func supportedInterfaceOrientations() -> Int {
    if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
        return Int(UIInterfaceOrientationMask.AllButUpsideDown.rawValue)
    } else {
        return Int(UIInterfaceOrientationMask.All.rawValue)
    }
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Release any cached data, images, etc that aren't in use.
}
}

When I exchange the ball and the cone in the code, I can see the ball and it disappears when tapping the screen, but the cone never appears. If you want to run this code, just create a new GameScene project in Xcode and copy this code into GameViewController.swift


Solution

  • Has anybody got the SCNMorpher to work?

    Yes, I've gotten SCNMorpher to work between both custom geometry and geometry that has been loaded from a file.

    The piece that you are missing is that all the morph targets need to have the same number of vertices and the vertices need to have the same arrangement. This is discussed in the documentation for the targets property:

    The base geometry and all target geometries must be topologically identical — that is, they must contain the same number and structural arrangement of vertices.

    Your example (morning between a sphere and a cone) doesn't fulfill this requirement and is not expected to work.

    You can try and morph between two different spheres (with the same number of segments!) to see that it does work.