Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stop sharing node's geometry with its clone programmatically

Tags:

swift

scenekit

When you create a copy of an object, geometry and its properties (materials...) are shared with that object.
In Xcode Scene Editor you can easily disable that by setting Geometry Sharing (under Attributes Inspector) to Unshare.

I want to achieve the same thing programmatically, but can't find any similar properties in SceneKit documentation.
I have found a similar post where someone proposed copying the object, its geometry and its material. I tried doing so but had no success.

This is the relevant part of my code:

let randomColors: [UIColor] = [UIColor.blue,  UIColor.red,  UIColor.yellow,  UIColor.gray]
let obstacleScene = SCNScene(named: "art.scnassets/Scenes/obstacleNormal.scn")
let obstacle = obstacleScene?.rootNode.childNode(withName: "obstacle", recursively: true)

for i in 1...15 {
    let randomPosition = SCNVector3(x: Float(i) * 3.5, y: 0.15, z: sign * Float(arc4random_uniform(UInt32(Int(playgroundZ/2 - 2.0))) + 1))
    let randomColor = randomColors[Int(arc4random_uniform(UInt32(3)))]

    let obstacleCopy = obstacle?.clone()
    obstacleCopy?.position = randomPosition
    obstacleCopy?.geometry?.materials.first?.diffuse.contents = randomColor
    obstacleCopy?.eulerAngles = SCNVector3(x: 10.0 * Float(i), y: Float(30 - i), z: 5.0 * Float(i) * sign) //malo na random

    obstacleCopy?.physicsBody = SCNPhysicsBody(type: .dynamic, shape: nil)
    obstacleCopy?.physicsBody?.isAffectedByGravity = false
    obstacleCopy?.physicsBody?.categoryBitMask = PhysicsCategory.obstacle
    obstacleCopy?.physicsBody?.collisionBitMask = PhysicsCategory.none
    obstacleCopy?.physicsBody?.contactTestBitMask = PhysicsCategory.car1 | PhysicsCategory.car2 | PhysicsCategory.barrier

    obstacleArray.append(obstacleCopy!)
    raceScene!.rootNode.addChildNode(obstacleCopy!)
}

I want to set different attributes on those objects but can't, because their geometry is shared.
I tried doing this with copying the object and cloning it, but I couldn't see any differences with copying or cloning it.

Is there a property which you can use to achieve geometry unsharing, similar to the option in the Scene Editor, OR should the method of also copying object's geometry and its materials work?

like image 320
Larisa Avatar asked Oct 06 '16 18:10

Larisa


2 Answers

According to the clone() API reference, you can copy the geometry after cloning, this will create a new unshared geometry for your node.

let newNode = node.clone()
newNode.geometry = node.geometry?.copy() as? SCNGeometry

The material attached to the copied geometry is still the same one being used on the original, so any changes will still affect both. Either create a new material, or make a copy.

if let newMaterial = newNode.geometry?.materials.first.copy() as? SCNMaterial {
    //make changes to material
    newNode.geometry?.materials = [newMaterial]
}
like image 183
James P Avatar answered Oct 08 '22 23:10

James P


You could deep clone a SCNNode, which creates an unshared geometry and unshared materials with the original.

fileprivate func deepCopyNode(node: SCNNode) -> SCNNode {
    let clone = node.clone()
    clone.geometry = node.geometry?.copy() as? SCNGeometry
    if let g = node.geometry {
        clone.geometry?.materials = g.materials.map{ $0.copy() as! SCNMaterial }
    }
    return clone
}
like image 28
Crashalot Avatar answered Oct 08 '22 22:10

Crashalot