user4432964
user4432964

Reputation:

SceneKit custom geometry

I am aware that SceneKit allows you to create certain basic shapes like box, sphere etc. I was wondering if you can create complex geometries using scene kit. I found this tutorial here and it was helpful. I exported some geometry BIMServer in json format. I want to take that and use it in SceneKit but I am having difficulties. Is it possible to do this in SceneKit? Below is a sample of some of the geometries i extracted.

{
  "id" : "2O2Fr$t4X7Zf8NOew3FNtn", 
  "type" : "IFCWALLSTANDARDCASE", 
  "geometry" : {
   "metadata" : { "formatVersion" : 3 }, 
    "materials": [],
    "vertices": [  8.8,0.2085,0.0, 3.4479692,0.2085,0.0, 3.4479692,0.2085,2.42, 2.6349692,0.2085,2.42, 2.6349692,0.2085,0.0, 0.0,0.2085,0.0, 0.0,0.2085,3.1, 8.8,0.2085,3.1, 8.383,0.2085,2.52, 8.383,0.2085,0.1, 3.548,0.2085,2.52, 3.548,0.2085,0.1, 8.8,0.2085,0.0, 3.4479692,0.2085,0.0, 3.4479692,-0.2085,0.0, 8.8,-0.2085,0.0, 3.4479692,-0.2085,2.42, 3.4479692,0.2085,2.42, 3.4479692,-0.2085,0.0, 3.4479692,0.2085,0.0, 2.6349692,-0.2085,2.42, 2.6349692,0.2085,2.42, 3.4479692,-0.2085,2.42, 3.4479692,0.2085,2.42, 2.6349692,-0.2085,0.0, 2.6349692,0.2085,0.0, 2.6349692,-0.2085,2.42, 2.6349692,0.2085,2.42, 2.6349692,0.2085,0.0, 0.0,0.2085,0.0, 0.0,-0.2085,0.0, 2.6349692,-0.2085,0.0, 0.0,0.2085,0.0, 0.0,0.2085,3.1, 0.0,-0.2085,0.0, 0.0,-0.2085,3.1, 8.8,0.2085,3.1, 0.0,0.2085,3.1, 0.0,-0.2085,3.1, 8.8,-0.2085,3.1, 8.8,-0.2085,0.0, 8.8,-0.2085,3.1, 8.8,0.2085,0.0, 8.8,0.2085,3.1, 8.383,-0.2085,2.52, 8.383,0.2085,2.52, 8.383,-0.2085,0.1, 8.383,0.2085,0.1, 3.548,-0.2085,2.52, 3.548,0.2085,2.52, 8.383,-0.2085,2.52, 8.383,0.2085,2.52, 8.383,-0.2085,0.1, 8.383,0.2085,0.1, 3.548,-0.2085,0.1, 3.548,0.2085,0.1, 3.548,-0.2085,0.1, 3.548,0.2085,0.1, 3.548,-0.2085,2.52, 3.548,0.2085,2.52, 0.0,-0.2085,0.0, 2.6349692,-0.2085,0.0, 2.6349692,-0.2085,2.42, 3.4479692,-0.2085,2.42, 3.4479692,-0.2085,0.0, 8.8,-0.2085,0.0, 8.8,-0.2085,3.1, 0.0,-0.2085,3.1, 8.383,-0.2085,0.1, 3.548,-0.2085,0.1, 8.383,-0.2085,2.52, 3.548,-0.2085,2.52   ], 
    "normals":  [ 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, -1.0,0.0,0.0, -1.0,0.0,0.0, -1.0,0.0,0.0, -1.0,0.0,0.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, -1.0,-0.0,-0.0, -1.0,-0.0,-0.0, -1.0,-0.0,-0.0, -1.0,-0.0,-0.0, 0.0,0.0,1.0, 0.0,0.0,1.0, 0.0,0.0,1.0, 0.0,0.0,1.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, -1.0,0.0,0.0, -1.0,0.0,0.0, -1.0,0.0,0.0, -1.0,0.0,0.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,1.0, 0.0,0.0,1.0, 0.0,0.0,1.0, 0.0,0.0,1.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0    ],
    "colors":   [ ],
    "uvs":    [ ],
    "faces": [  32, 8,7,0,8,7,0, 32, 8,0,9,8,0,9, 32, 7,8,10,7,8,10, 32, 10,2,3,10,2,3, 32, 2,10,11,2,10,11, 32, 11,9,1,11,9,1, 32, 7,10,6,7,10,6, 32, 10,3,6,10,3,6, 32, 3,4,5,3,4,5, 32, 6,3,5,6,3,5, 32, 9,0,1,9,0,1, 32, 11,1,2,11,1,2, 32, 14,12,15,14,12,15, 32, 13,12,14,13,12,14, 32, 18,16,17,18,16,17, 32, 18,17,19,18,17,19, 32, 22,20,21,22,20,21, 32, 22,21,23,22,21,23, 32, 26,24,25,26,24,25, 32, 26,25,27,26,25,27, 32, 29,31,30,29,31,30, 32, 29,28,31,29,28,31, 32, 33,32,34,33,32,34, 32, 35,33,34,35,33,34, 32, 38,39,37,38,39,37, 32, 39,36,37,39,36,37, 32, 41,40,42,41,40,42, 32, 43,41,42,43,41,42, 32, 46,44,45,46,44,45, 32, 46,45,47,46,45,47, 32, 50,48,49,50,48,49, 32, 50,49,51,50,49,51, 32, 55,52,53,55,52,53, 32, 54,52,55,54,52,55, 32, 58,56,57,58,56,57, 32, 58,57,59,58,57,59, 32, 67,60,62,67,60,62, 32, 62,63,71,62,63,71, 32, 67,62,71,67,62,71, 32, 62,60,61,62,60,61, 32, 71,63,69,71,63,69, 32, 67,71,66,67,71,66, 32, 66,71,70,66,71,70, 32, 69,64,68,69,64,68, 32, 70,68,65,70,68,65, 32, 66,70,65,66,70,65, 32, 69,63,64,69,63,64, 32, 68,64,65,68,64,65 ]
     }

  } ,{
  "id" : "2O2Fr$t4X7Zf8NOew3FKGS", 
  "type" : "IFCWALLSTANDARDCASE", 
  "geometry" : {
   "metadata" : { "formatVersion" : 3 }, 
    "materials": [],
    "vertices": [  3.583,0.062,0.0, 3.583,0.062,2.795, 6.661338E-16,0.062,0.0, 6.661338E-16,0.062,2.795, 6.661338E-16,0.062,0.0, 6.661338E-16,0.062,2.795, 6.661338E-16,-0.062,0.0, 6.661338E-16,-0.062,2.795, 6.661338E-16,-0.062,0.0, 6.661338E-16,-0.062,2.795, 3.583,-0.062,0.0, 3.583,-0.062,2.795, 3.583,-0.062,0.0, 3.583,-0.062,2.795, 3.583,0.062,0.0, 3.583,0.062,2.795, 3.583,0.062,0.0, 6.661338E-16,0.062,0.0, 6.661338E-16,-0.062,0.0, 3.583,-0.062,0.0, 3.583,0.062,2.795, 6.661338E-16,0.062,2.795, 6.661338E-16,-0.062,2.795, 3.583,-0.062,2.795   ], 
    "normals":  [ 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, -1.0,-0.0,-0.0, -1.0,-0.0,-0.0, -1.0,-0.0,-0.0, -1.0,-0.0,-0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,1.0, 0.0,0.0,1.0, 0.0,0.0,1.0, 0.0,0.0,1.0    ],
    "colors":   [ ],
    "uvs":    [ ],
    "faces": [  32, 1,0,2,1,0,2, 32, 3,1,2,3,1,2, 32, 5,4,6,5,4,6, 32, 7,5,6,7,5,6, 32, 9,8,10,9,8,10, 32, 11,9,10,11,9,10, 32, 13,12,14,13,12,14, 32, 15,13,14,15,13,14, 32, 17,19,18,17,19,18, 32, 17,16,19,17,16,19, 32, 22,23,21,22,23,21, 32, 23,20,21,23,20,21 ]
     }

Upvotes: 1

Views: 1354

Answers (2)

norman784
norman784

Reputation: 2221

I know is a bit too late, but if someone else like me find this will be cool to have a better answer, for those who want to understand how SceneKit custom geometry works, I see some issues with the geometry but really don't know how must looks like, this code is far from perfect but is a start point.

class JSONGeometry: Decodable {
    class Geometry: Decodable {
        var vertices: [CGFloat]
        var normals: [CGFloat]
        var faces: [Int16]

        init(vertices: [CGFloat], normals: [CGFloat], faces: [Int16]) {
            self.vertices = vertices
            self.normals = normals
            self.faces = faces
        }
    }

    static let data: [JSONGeometry] = {
        let json = """
[{
  "id" : "2O2Fr$t4X7Zf8NOew3FNtn",
  "type" : "IFCWALLSTANDARDCASE",
  "geometry" : {
    "metadata" : { "formatVersion" : 3 },
    "materials": [],
    "vertices": [  8.8,0.2085,0.0, 3.4479692,0.2085,0.0, 3.4479692,0.2085,2.42, 2.6349692,0.2085,2.42, 2.6349692,0.2085,0.0, 0.0,0.2085,0.0, 0.0,0.2085,3.1, 8.8,0.2085,3.1, 8.383,0.2085,2.52, 8.383,0.2085,0.1, 3.548,0.2085,2.52, 3.548,0.2085,0.1, 8.8,0.2085,0.0, 3.4479692,0.2085,0.0, 3.4479692,-0.2085,0.0, 8.8,-0.2085,0.0, 3.4479692,-0.2085,2.42, 3.4479692,0.2085,2.42, 3.4479692,-0.2085,0.0, 3.4479692,0.2085,0.0, 2.6349692,-0.2085,2.42, 2.6349692,0.2085,2.42, 3.4479692,-0.2085,2.42, 3.4479692,0.2085,2.42, 2.6349692,-0.2085,0.0, 2.6349692,0.2085,0.0, 2.6349692,-0.2085,2.42, 2.6349692,0.2085,2.42, 2.6349692,0.2085,0.0, 0.0,0.2085,0.0, 0.0,-0.2085,0.0, 2.6349692,-0.2085,0.0, 0.0,0.2085,0.0, 0.0,0.2085,3.1, 0.0,-0.2085,0.0, 0.0,-0.2085,3.1, 8.8,0.2085,3.1, 0.0,0.2085,3.1, 0.0,-0.2085,3.1, 8.8,-0.2085,3.1, 8.8,-0.2085,0.0, 8.8,-0.2085,3.1, 8.8,0.2085,0.0, 8.8,0.2085,3.1, 8.383,-0.2085,2.52, 8.383,0.2085,2.52, 8.383,-0.2085,0.1, 8.383,0.2085,0.1, 3.548,-0.2085,2.52, 3.548,0.2085,2.52, 8.383,-0.2085,2.52, 8.383,0.2085,2.52, 8.383,-0.2085,0.1, 8.383,0.2085,0.1, 3.548,-0.2085,0.1, 3.548,0.2085,0.1, 3.548,-0.2085,0.1, 3.548,0.2085,0.1, 3.548,-0.2085,2.52, 3.548,0.2085,2.52, 0.0,-0.2085,0.0, 2.6349692,-0.2085,0.0, 2.6349692,-0.2085,2.42, 3.4479692,-0.2085,2.42, 3.4479692,-0.2085,0.0, 8.8,-0.2085,0.0, 8.8,-0.2085,3.1, 0.0,-0.2085,3.1, 8.383,-0.2085,0.1, 3.548,-0.2085,0.1, 8.383,-0.2085,2.52, 3.548,-0.2085,2.52   ],
    "normals":  [ 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, -1.0,0.0,0.0, -1.0,0.0,0.0, -1.0,0.0,0.0, -1.0,0.0,0.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, -1.0,-0.0,-0.0, -1.0,-0.0,-0.0, -1.0,-0.0,-0.0, -1.0,-0.0,-0.0, 0.0,0.0,1.0, 0.0,0.0,1.0, 0.0,0.0,1.0, 0.0,0.0,1.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, -1.0,0.0,0.0, -1.0,0.0,0.0, -1.0,0.0,0.0, -1.0,0.0,0.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,1.0, 0.0,0.0,1.0, 0.0,0.0,1.0, 0.0,0.0,1.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0    ],
    "colors":   [ ],
    "uvs":    [ ],
    "faces": [  32, 8,7,0,8,7,0, 32, 8,0,9,8,0,9, 32, 7,8,10,7,8,10, 32, 10,2,3,10,2,3, 32, 2,10,11,2,10,11, 32, 11,9,1,11,9,1, 32, 7,10,6,7,10,6, 32, 10,3,6,10,3,6, 32, 3,4,5,3,4,5, 32, 6,3,5,6,3,5, 32, 9,0,1,9,0,1, 32, 11,1,2,11,1,2, 32, 14,12,15,14,12,15, 32, 13,12,14,13,12,14, 32, 18,16,17,18,16,17, 32, 18,17,19,18,17,19, 32, 22,20,21,22,20,21, 32, 22,21,23,22,21,23, 32, 26,24,25,26,24,25, 32, 26,25,27,26,25,27, 32, 29,31,30,29,31,30, 32, 29,28,31,29,28,31, 32, 33,32,34,33,32,34, 32, 35,33,34,35,33,34, 32, 38,39,37,38,39,37, 32, 39,36,37,39,36,37, 32, 41,40,42,41,40,42, 32, 43,41,42,43,41,42, 32, 46,44,45,46,44,45, 32, 46,45,47,46,45,47, 32, 50,48,49,50,48,49, 32, 50,49,51,50,49,51, 32, 55,52,53,55,52,53, 32, 54,52,55,54,52,55, 32, 58,56,57,58,56,57, 32, 58,57,59,58,57,59, 32, 67,60,62,67,60,62, 32, 62,63,71,62,63,71, 32, 67,62,71,67,62,71, 32, 62,60,61,62,60,61, 32, 71,63,69,71,63,69, 32, 67,71,66,67,71,66, 32, 66,71,70,66,71,70, 32, 69,64,68,69,64,68, 32, 70,68,65,70,68,65, 32, 66,70,65,66,70,65, 32, 69,63,64,69,63,64, 32, 68,64,65,68,64,65 ]
     }

} ,{
  "id" : "2O2Fr$t4X7Zf8NOew3FKGS",
  "type" : "IFCWALLSTANDARDCASE",
  "geometry" : {
    "metadata" : { "formatVersion" : 3 },
    "materials": [],
    "vertices": [  3.583,0.062,0.0, 3.583,0.062,2.795, 6.661338E-16,0.062,0.0, 6.661338E-16,0.062,2.795, 6.661338E-16,0.062,0.0, 6.661338E-16,0.062,2.795, 6.661338E-16,-0.062,0.0, 6.661338E-16,-0.062,2.795, 6.661338E-16,-0.062,0.0, 6.661338E-16,-0.062,2.795, 3.583,-0.062,0.0, 3.583,-0.062,2.795, 3.583,-0.062,0.0, 3.583,-0.062,2.795, 3.583,0.062,0.0, 3.583,0.062,2.795, 3.583,0.062,0.0, 6.661338E-16,0.062,0.0, 6.661338E-16,-0.062,0.0, 3.583,-0.062,0.0, 3.583,0.062,2.795, 6.661338E-16,0.062,2.795, 6.661338E-16,-0.062,2.795, 3.583,-0.062,2.795   ],
    "normals":  [ 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, 0.0,1.0,0.0, -1.0,-0.0,-0.0, -1.0,-0.0,-0.0, -1.0,-0.0,-0.0, -1.0,-0.0,-0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 0.0,-1.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 1.0,0.0,0.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,-1.0, 0.0,0.0,1.0, 0.0,0.0,1.0, 0.0,0.0,1.0, 0.0,0.0,1.0    ],
    "colors":   [ ],
    "uvs":    [ ],
    "faces": [  32, 1,0,2,1,0,2, 32, 3,1,2,3,1,2, 32, 5,4,6,5,4,6, 32, 7,5,6,7,5,6, 32, 9,8,10,9,8,10, 32, 11,9,10,11,9,10, 32, 13,12,14,13,12,14, 32, 15,13,14,15,13,14, 32, 17,19,18,17,19,18, 32, 17,16,19,17,16,19, 32, 22,23,21,22,23,21, 32, 23,20,21,23,20,21 ]
  }
}]
"""
        guard let data = json.data(using: .utf8) else { return [] }
        guard let result = try? JSONDecoder().decode([JSONGeometry].self, from: data) else { return [] }
        return result
    }()

    var id: String
    var type: String
    var geometry: Geometry

    var toSCN: SCNGeometry {
        var sources: [SCNGeometrySource] = []

        let vertices = SCNGeometrySource(
            vertices: geometry.vertices.eachSlice(3).map{ SCNVector3($0[0], $0[1], $0[2]) }
        )
        sources.append(vertices)

        if geometry.normals.count > 0 {
            let normals = SCNGeometrySource(
                normals: geometry.normals.eachSlice(3).map{ SCNVector3($0[0], $0[1], $0[2]) }
            )
            sources.append(normals)
        }

        let indices = Data(
            bytes: geometry.faces.map{ Int16($0) },
            count: MemoryLayout<Int16>.size * geometry.faces.count
        )

        let elements = [
            SCNGeometryElement(
                data: indices,
                primitiveType: .triangles,
                primitiveCount: geometry.faces.count / 3,
                bytesPerIndex: MemoryLayout<Int16>.size
            )
        ]

        return SCNGeometry(sources: sources, elements: elements)
    }

    init(id: String, type: String, geometry: Geometry) {
        self.id = id
        self.type = type
        self.geometry = geometry
    }
}

extension Sequence {
    func eachSlice(_ clump:Int) -> [[Self.Element]] {
        return self.reduce(into:[]) { memo, cur in
            if memo.count == 0 {
                return memo.append([cur])
            }
            if memo.last!.count < clump {
                memo.append(memo.removeLast() + [cur])
            } else {
                memo.append([cur])
            }
        }
    }
}

I've a gist where I will update a code similar to this, because theres some thinks that I don't understand quite well right now like UV Mapping and normals (don't really now whats for).

Regards

Upvotes: 2

bpedit
bpedit

Reputation: 1196

Ouch! SceneKit is great but, besides the few primitives it offers, it is not a modeler. It's far easier and better in every way to create models in an app dedicated to 3-D modeling. A lot of folks on the forum use Blender, free and redily available. I use Cinema 4D, many other options are available. The only necessity is that they output DAE (Collada) files.

The learning curve in using such an app is nothing compared to the frustration you will most likely experience using such low-level construction techniques.

Once you import a DAE file into Xcode it can be inspected in the editor window. You can move nodes, rearrange then, rename them and delete them. More on that: Transform and Rotate in Scenekit

Upvotes: 0

Related Questions