Search code examples
iosswift3dscenekitscnscene

How can I create a 3D Grid in scenekit


how can I make a grid with 3d objects(box). I already know how to setup scnscene and how to create an object. But I don't know how to make the layout. The grid should look like this one, with 3d object in a 3D space.

Here's what I tried:

           convenience init(create: Bool) {
        self.init()

        let geometry = SCNBox(width: 0.8 , height: 0.8,
                              length: 0.1, chamferRadius: 0.005)
        geometry.firstMaterial?.diffuse.contents = UIColor.red
        geometry.firstMaterial?.specular.contents = UIColor.white
        geometry.firstMaterial?.emission.contents = UIColor.blue
        let offset: Int = 10

        for xIndex:Int in 0...2 {
            for yIndex:Int in 0...2 {
                // create a geometry copy
                let geoCopy = geometry.copy() as! SCNGeometry

                var images:[UIImage]=[]
                for i in 1...5 {
                    if let img = UIImage(named: "\(i)"){
                        images.append(img)
                        let material = SCNMaterial()
                        material.diffuse.contents = img
                        geoCopy.firstMaterial = material

                    }
                }

                let boxnode = SCNNode(geometry: geoCopy)
                let boxCopy = boxnode.copy() as! SCNNode
                boxCopy.position.x = Float(xIndex - offset)
                boxCopy.position.y = Float(yIndex - offset)
                self.rootNode.addChildNode(boxCopy)
            }
        }
    }

But I only see one box.

Thanks!

Picture of my Images:

enter image description here


Solution

  • You need to create one geometry, one box node and then copy that boxNode. You use clone when you have node with children and flattenedClone when you want to merge geometries/materials of the entire subtree at the node. In your case, copy should suffice. Just change the position of your copied node.

    GameScene

    import Foundation
    import SceneKit
    
    class GameScene: SCNScene {
    
        override init() {
            super.init()
    
            let geometry = SCNBox(width: 0.6 , height: 0.6,
                                   length: 0.1, chamferRadius: 0.005)
            geometry.firstMaterial?.diffuse.contents = UIColor.red
            geometry.firstMaterial?.specular.contents = UIColor.white
            geometry.firstMaterial?.emission.contents = UIColor.blue
            let boxnode = SCNNode(geometry: geometry)
            let offset: Int = 16
    
            for xIndex:Int in 0...32 {
                for yIndex:Int in 0...32 {
                    let boxCopy = boxnode.copy() as! SCNNode
                    boxCopy.position.x = Float(xIndex - offset)
                    boxCopy.position.y = Float(yIndex - offset)
                    self.rootNode.addChildNode(boxCopy)
                }
            }
        }
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
        }
    }
    

    In your view controller, viewDidLoad:

    override func viewDidLoad() {
            super.viewDidLoad()
    
            // create a new scene
            let scene = GameScene()
    
            // retrieve the SCNView
            let scnView = self.view as! SCNView
    
            // set the scene to the view
            scnView.scene = scene
            scnView.pointOfView?.position = SCNVector3Make(0, 0, 100)
    
            // allows the user to manipulate the camera
            scnView.allowsCameraControl = true
    
            // show statistics such as fps and timing information
            scnView.showsStatistics = true
    
            // configure the view
             scnView.backgroundColor = UIColor.white
    
        }
    

    Note that I have just pushed the camera point of view back on the +Z axis to have a better view of your grid.

    The grid screenshot

    enter image description here


    Edit: New material for each geometry

    If you want to assign a new material to each geometry, you need to create a copy of the geometry and assign a new material to that geometry copy. See the code below which randomly assign a UIImage for each diffuse property, from a set of seven images named 1.png to 8.png.

    import Foundation
    import SceneKit
    
    class GameScene: SCNScene {
    
        override init() {
            super.init()
    
            let geometry = SCNBox(width: 6 , height: 6,
                                  length: 6, chamferRadius: 0.5)
    
            for xIndex:Int in stride(from: 0, to: 32, by:8) {
                for yIndex:Int in stride(from: 0, to: 32, by: 8) {
                    // create a geometry copy
                    let geoCopy = geometry.copy() as! SCNGeometry
    
                    // create a random material
                    let r = arc4random_uniform(7) + 1
                    let img = UIImage(named: "\(r).png")
                    let mat = SCNMaterial()
                    mat.diffuse.contents = img
                    geoCopy.firstMaterial = mat
    
                    // create a copy node with new material and geo copy
                    let boxnode = SCNNode(geometry: geoCopy)
                    let boxCopy = boxnode.copy() as! SCNNode
                    boxCopy.position.x = Float(xIndex - offset)
                    boxCopy.position.y = Float(yIndex - offset)
                    self.rootNode.addChildNode(boxCopy)
                }
            }
        }
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
        }
    }
    

    Screenshot

    enter image description here