2

I'm building an augmented-reality iPhone application, and it will require a large amount of SceneKit nodes to be rendered. I want to integrate the Flyweight design pattern described in Design Patterns by Gamma, Helm, Johnson, and Vlissides. Also tutorial here Flyweight Design Pattern However, I'm running into issues with the implementation and how Swift handles objects.

TL;DR: Is there a way in Swift where I can add the same SCNNode to a ARSCNView more than once and have it be displayed in different positions?

I have a class called Box which builds SCNBoxs which I want to leverage Flyweight on; the intrinsic state of a Box is the dimensions. The extrinsic state is its color and position.

Box Class

class Box {
    var size:CGFloat
    var position:(Float, Float, Float)
    var color:UIColor
    var node:SCNNode!

    init(color:UIColor){
        self.color = color
        /*Set the following below as default values*/
        self.size = CGFloat(0.05) //Side length
        self.position = (0,0,0)
        self.createBox()
    }

    private func createBox() -> Void {
        /*Creating box and setting its color*/
        self.node = SCNNode(geometry: SCNBox(width: size, height: size, length: size, chamferRadius: 0))
        self.node.geometry?.firstMaterial?.diffuse.contents = self.color
    }

    func draw(sceneView: ARSCNView) -> Void {
        sceneView.scene.rootNode.addChildNode(self.node)
    }
}

I have a factory class which implements the design with a dictionary checking if previous objects of the same color, if so reuse the object, else create a new one.

Factory Class

class BoxFactory{
    var hash:[UIColor:Box] = [UIColor:Box]()
    func getBox(color c:UIColor) -> Box {
        /*Check if box of color c already exists*/
        if(self.hash[c] != nil){
            return self.hash[c]!
        }
        /*Create a new box if it does not*/
        let b:Box = Box(color: c)
        self.hash[c] = b
        return b
    }
}

And some view controller which hold an ARSCNView object to display the boxes.

let factory:BoxFactory = BoxFactory()

/*Create two boxes of same color. One on the left and on the right*/
let leftBox:Box = factory.getBox(color: UIColor.green)
leftBox.position =  SCNVector3(-0.1,0,0)
leftBox.draw(sceneView: self.sceneView)

let rightBox:Box = factory.getBox(color: UIColor.green)
rightBox.position =  SCNVector3(0.1,0,0)
rightBox.draw(sceneView: self.sceneView)

However, this only produces one green box; the right one. The second getBox() call overrides the first's position Image below. Does anyone have any advice, for the implementation of this pattern, or Swift, that can help achieve this pattern of object reuse?

*Side Note: I don't necessary want to hash the object's position along with its color. Being that I'll have many Boxes of different color and position, it'll defeat the whole idea of reusing the same object.

Only one green box

Miket25
  • 1,895
  • 3
  • 15
  • 29
  • I think I can help you on this but, before I need to know a couple of things. Does the box have a lifecycle? will it be "removed" or will persist until the end of the app? – mugx Dec 28 '17 at 02:30
  • @AndreaMugnaini Some boxes will be removed during runtime, and some will survive to the end. It depends on the user's interaction with the boxes. – Miket25 Dec 28 '17 at 02:44
  • 1
    I think, since your boxes will be visible at the same time, you should use `clone()` or `flattenedClone()`: https://developer.apple.com/documentation/scenekit/scnnode/1408046-clone https://developer.apple.com/documentation/scenekit/scnnode/1407960-flattenedclone – mugx Dec 28 '17 at 05:12

1 Answers1

1

SceneKit already handles that for you. It automatically caches the geometry's mesh so that it can efficiently be reused across geometries, and you can share materials across geometries as well. If you happen to have duplicated materials that lead to the same shader, SceneKit will compile it only once.

mnuages
  • 13,049
  • 2
  • 23
  • 40