Search code examples
swiftscenekit

SceneKit - Replace material to an array of nodes


Goal: add default material to all child node on a Scenekit scene.

What I did:

// get child noses from scene
let allNodes = scene.rootNode.childNodes

//create a defaul material
let defaultMaterial = SCNMaterial()  
defaultMaterial.diffuse.contents = NSColor.red
         
//add defaul material to all child nodes
allChildNodes.replaceMaterial(at: 0, with: defaultMaterial)

Problem:

Looks like "replaceMaterial" doesn't work with an array of nodes.

Question:

How I add a material to an array of nodes?


Solution

  • Applying a material to an array of nodes

    Try this code to achieve your goal:

    import SceneKit
    
    class ViewController : NSViewController {
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            let sceneView = self.view as! SCNView
            let scene = SCNScene(named: "art.scnassets/scene.scn")!
            sceneView.scene = scene
            
            let material = SCNMaterial()
            material.diffuse.contents = NSColor.systemYellow
            
            let allNodes = scene.rootNode.childNodes[0].childNodes
                    
            for i in 0...(allNodes.count-1) {
                allNodes[i].geometry?.firstMaterial = material
            }
        }
    }
    

    enter image description here

    enter image description here



    Applying a material to an array of nodes

    Also, you can apply any random color to any model with one click.

    import SwiftUI
    import SceneKit
    
    struct ContentView : View { 
        let scene = SCNScene()
        let opts: SceneView.Options =  [.allowsCameraControl, .autoenablesDefaultLighting]
        var body: some View {
            ZStack {
                SceneView(scene: scene, options: opts).ignoresSafeArea()
                self.createModel()                
                VStack {
                    Spacer()
                    Button("Update Material Color") {
                        let material = scene.rootNode.childNodes[0].geometry?.materials[0]
                        material?.diffuse.contents = randomColor()
                    }
                }
            }
        }
        func createModel() -> EmptyView {
            let node = SCNNode(geometry: SCNSphere(radius: 0.25))
            node.geometry?.materials[0].diffuse.contents = randomColor()
            node.geometry?.materials[0].lightingModel = .physicallyBased
            scene.rootNode.addChildNode(node)
            scene.background.contents = NSColor.black
            return EmptyView()
        }
        func randomColor() -> NSColor {
            return NSColor(red: .random(in: 0...1), green: .random(in: 0...1),
                          blue: .random(in: 0...1), alpha: 1.0)
        }
    }
    

    enter image description here