3

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

John
  • 799
  • 6
  • 22

2 Answers2

2

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

3d-indiana-jones
  • 879
  • 7
  • 17
  • thank you!! But I have on last question. I'm trying to add a SCNMaterial() on each geometry, how can I do it? The material is different for every node I just have 15 nodes. Thanks. I just updated my question. – John Dec 23 '16 at 06:41
  • @John Updated the answer so that each node has a different material. – 3d-indiana-jones Dec 23 '16 at 08:48
  • Thanks, really appreciated. But how can I do it with images. Just updated my answer. – John Dec 23 '16 at 14:44
  • The contents of a material property can be any of color, image or even animated content. So setting `mat.diffuse.contents = UIImage(named:"some.png") will also work. You can assign a separate image for each node, just as we are doing with colors. Hope this helps. See: https://developer.apple.com/reference/scenekit/scnmaterialproperty/1395372-contents – 3d-indiana-jones Dec 23 '16 at 16:46
  • I tried but I see the same image on each geometry. check my question. Thanks – John Dec 23 '16 at 16:48
  • There is something wrong with your images then. See my updated answer. I assign random image from a set of seven images and they work fine. My suspicion is that every time you are reading image for i = 1 and hence seeing the same image. – 3d-indiana-jones Dec 23 '16 at 17:12
  • It's working when I use the "let r = arc4random_uniform(7) + 1" but it's not working with the For loop. Can you try it just one last time with the For loop please? Just one last time. Thanks – John Dec 23 '16 at 17:31
  • I tried with your code and as I suspected, it is always image number 5 that is applied. Your code also clearly indicates that you loop from 1 to 5 and as all 1.png to 5.png exist, it is 5.png that is applied to each node. Basically you are overwriting each node's material with images 1.png to 5.png, always ending with 5.png. You need to fix your code so that each node gets a different image. The problem is not with UIImage or anything, but with your logic that applies the image to the material. – 3d-indiana-jones Dec 23 '16 at 18:15
  • I understand, but somehow I still can't find a way to put it in code. If I don't use a For Loop what can I use? Sorry I'm a beginner. Thanks – John Dec 24 '16 at 19:29
  • How to create a 3d floor like attached image in scenekit https://blog.pusher.com/wp-content/uploads/2017/08/building-ar-with-arkit-and-scenekit-10-blender-cube.png – madhuiOS Feb 13 '18 at 06:14
0

You need to put the statement -let boxnode = SCNNode(geometry: self.geometry) - inside the loop. If you wish to use the same materials, you can use the same geometry for all nodes (just store the geometry in a variable and assign it). Otherwise, if you wish to have different materials, copy the geometry and assign different materials each time.

Karl Sigiscar
  • 289
  • 1
  • 7
  • Thanks @Karl Sigiscar, I just updated my question. But it doesn't work. I still see just one box. Can you help me? – John Dec 22 '16 at 15:50
  • 1
    You also need to move the addChildNode part of your code inside the loop. Each new node created during iteration must be added to your scene. – Karl Sigiscar Dec 22 '16 at 16:13