Search code examples
swiftscenekit

How to use SCNSceneSource.LoadingOption.createNormalsIfAbsent?


How to use SCNSceneSource.LoadingOption.createNormalsIfAbsent?

The file prim.scn has a simple toroid geometry that has no normals. When I load and inspect it it still has no normals. I've also tried with an empty SCNGeometrySource for normals. It doesn't add them.

scnScene = SCNScene()
let documentsPath = FileManager.default.urls(for: .documentDirectory, 
                                              in: .userDomainMask).first!
let url = documentsPath.appendingPathComponent("prim.scn")

do {
    scnScene = try SCNScene(url: url,
                        options: [.createNormalsIfAbsent: true])
} catch {
    print("error")
}

Inspecting in lldb yields:

(lldb) p scnScene.rootNode.childNodes[3].geometry?.sources
([SCNGeometrySource]?) $R1 = 1 value {
  [0] = 0x0000600001a09f80
}
(lldb) p scnScene.rootNode.childNodes[3].geometry?.sources[0]
(SCNGeometrySource?) $R2 = 0x0000600001a09f80 {
  baseNSObject@0 = {
    isa = SCNGeometrySource
  }
  _data = 0x00007fdef1847c00 4800 bytes
  _semantic = 0x00007ff9540c3ec0 "kGeometrySourceSemanticVertex"
  _vectorCount = 400
  _componentType = 1
  _componentCount = 3
  _colorSpace = 0x0000000000000000
  _dataOffset = 0
  _dataStride = 12
  _mkSemantic = '\0'
  _mtlBuffer = 0x0000000000000000
  _mtlVertexFormat = 0
  _encodeDataAsHalf = '\0'
}

I would expect there to be a

scnScene.rootNode.childNodes[3].geometry?.sources[0] 

containing the normals.

Here's prim.scn file.


Solution

  • You already got info about normals count in your model. The names of parameters are _vectorCount and _componentCount. Use the following code to calculate the number of normals. For additional info, read this post.

    macOS 13.0 Ventura, Xcode 14.1 RC 2.

    import SceneKit
    
    class ViewController : UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            
            let sceneView = self.view as! SCNView
            sceneView.autoenablesDefaultLighting = true
            sceneView.backgroundColor = .black
    
            let path = FileManager.default.urls(for: .documentDirectory,
                                                 in: .userDomainMask).first!
            let url = path.appendingPathComponent("prim.scn")
    
            do {
                let scene = try SCNScene(url: url,
                                     options: [.createNormalsIfAbsent: true])
                sceneView.scene = scene
    
                let sceneNodes: [SCNNode] = scene.rootNode.childNodes
    
                for s in (sceneNodes[3].geometry?.sources.enumerated())! {
                    // 1200 normals (400 vectors * 3 normals)
                    print(s.element.vectorCount * s.element.componentsPerVector)
                }
            } catch {
                print(error.localizedDescription)
            }            
        }
    }
    

    P. S.

    Your model initially has normals – that's why [.createNormalsIfAbsent: true] has no effect.

    SceneKit's directional light wouldn't work without normals.

    enter image description here