4
votes

I'm trying to create a custom SCNGeometry in the form of a plane with custom shape, which could be placed in an ARKit session. I'm using the option SCNGeometryPrimitiveTypePolygon in the following method which seems to work fine:

extension SCNGeometry {

    static func polygonPlane(vertices: [SCNVector3]) -> SCNGeometry {

        var indices: [Int32] = [Int32(vertices.count)]

        var index: Int32 = 0
        for _ in vertices {
            indices.append(index)
            index += 1
        }
        let vertexSource = SCNGeometrySource(vertices: vertices)
        let indexData = Data(bytes: indices, count: indices.count * MemoryLayout<Int32>.size)
        let element = SCNGeometryElement(data: indexData, primitiveType: .polygon, primitiveCount: 1, bytesPerIndex: MemoryLayout<Int32>.size)
        let geometry = SCNGeometry(sources: [vertexSource], elements: [element])
        let material = SCNMaterial()
        material.diffuse.contents = UIColor.blue
        material.isDoubleSided = true
        geometry.firstMaterial = material
        return geometry
    }

After creating this geometry I assign it to a SCNNode:s .geometry property and add it to my AR scene as usual:

let geometry = SCNGeometry.polygonPlane(vertices: verticesArray)
let node = SCNNode(geometry: geometry)
sceneView.scene.rootNode.addChildNode(node)

This works well for some types of plane shapes. However I often get a crash, mostly when using complex shapes or many vertices to outline the plane shape. I've experimented and it seems as the geometry is created as expected without any errors, but the error occurs when the node is added to the scene and about to be rendered. This is the printed error:

-[MTLDebugDevice validateNewBufferArgs:options:]:467: failed assertion `Cannot create buffer of zero length.'

What appears in the debug navigator:

Debug navigator

And finally the stack trace:

* thread #17, name = 'com.apple.scenekit.scnview-renderer', queue = 'com.apple.scenekit.renderingQueue.ARSCNView0x11be03ed0', stop reason = signal SIGABRT
  * frame #0: 0x0000000219cad0cc libsystem_kernel.dylib`__pthread_kill + 8
    frame #1: 0x0000000219d23a88 libsystem_pthread.dylib`pthread_kill + 300
    frame #2: 0x0000000219c0614c libsystem_c.dylib`abort + 144
    frame #3: 0x0000000219bd3274 libsystem_c.dylib`__assert_rtn + 224
    frame #4: 0x000000021c28e23c Metal`MTLReportFailure + 528
    frame #5: 0x000000023f984108 MetalTools`-[MTLDebugDevice validateNewBufferArgs:options:] + 172
    frame #6: 0x000000023f98430c MetalTools`-[MTLDebugDevice newBufferWithBytes:length:options:] + 128
    frame #7: 0x000000022e323b48 SceneKit`-[SCNMTLResourceManager _bufferForData:bytesPerIndex:] + 404
    frame #8: 0x000000022e323f14 SceneKit`-[SCNMTLResourceManager renderResourceForMeshElement:] + 416
    frame #9: 0x000000022e3243c0 SceneKit`-[SCNMTLResourceManager renderResourceForMesh:dataKind:] + 692
    frame #10: 0x000000022e364980 SceneKit`_execute(SCNMTLRenderContext*, DrawCommand) + 916
    frame #11: 0x000000022e3644a0 SceneKit`-[SCNMTLRenderContext drawRenderElement:withPass:] + 608
    frame #12: 0x000000022e3630d4 SceneKit`-[SCNMTLRenderContext processRendererElements:count:engineIterationContext:] + 1044
    frame #13: 0x000000022e4afc54 SceneKit`C3D::DrawNodesPass::_renderEye(long) + 472
    frame #14: 0x000000022e4afa00 SceneKit`C3D::DrawNodesPass::execute(C3D::RenderArgs const&) + 260
    frame #15: 0x000000022e54a1b4 SceneKit`C3D::MainPass::execute(C3D::RenderArgs const&) + 176
    frame #16: 0x000000022e318904 SceneKit`C3D::__renderSlice(C3D::RenderGraph*, C3D::RenderPass*, unsigned short&, C3D::RenderGraph::GraphNode const&, C3D::RenderGraph::Stage*&, C3D::RenderArgs) + 1156
    frame #17: 0x000000022e319cd0 SceneKit`C3D::RenderGraph::execute() + 3896
    frame #18: 0x000000022e42fd8c SceneKit`-[SCNRenderer _renderSceneWithEngineContext:sceneTime:] + 2364
    frame #19: 0x000000022e42ff44 SceneKit`-[SCNRenderer _drawSceneWithNewRenderer:] + 312
    frame #20: 0x000000022e43056c SceneKit`-[SCNRenderer _drawScene:] + 72
    frame #21: 0x000000022e4309bc SceneKit`-[SCNRenderer _drawAtTime:] + 760
    frame #22: 0x000000022e4dfecc SceneKit`-[SCNView _drawAtTime:] + 492
    frame #23: 0x000000022e37c15c SceneKit`__69-[NSObject(SCN_DisplayLinkExtensions) SCN_setupDisplayLinkWithQueue:]_block_invoke + 60
    frame #24: 0x000000022e4a1c50 SceneKit`__36-[SCNDisplayLink _callbackWithTime:]_block_invoke + 88
    frame #25: 0x0000000104d74778 libdispatch.dylib`_dispatch_client_callout + 20
    frame #26: 0x0000000104d82fc0 libdispatch.dylib`_dispatch_lane_barrier_sync_invoke_and_complete + 160
    frame #27: 0x000000022e4a1bb8 SceneKit`-[SCNDisplayLink _callbackWithTime:] + 268
    frame #28: 0x0000000104e082d4 GPUToolsCore`-[DYDisplayLinkInterposer forwardDisplayLinkCallback:] + 204
    frame #29: 0x000000021e53aea8 QuartzCore`CA::Display::DisplayLink::dispatch_items(unsigned long long, unsigned long long, unsigned long long) + 632
    frame #30: 0x000000021e608858 QuartzCore`display_timer_callback(__CFMachPort*, void*, long, void*) + 276
    frame #31: 0x000000021a083058 CoreFoundation`__CFMachPortPerform + 192
    frame #32: 0x000000021a0aaaf0 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 60
    frame #33: 0x000000021a0aa1e8 CoreFoundation`__CFRunLoopDoSource1 + 444
    frame #34: 0x000000021a0a4d80 CoreFoundation`__CFRunLoopRun + 2060
    frame #35: 0x000000021a0a4254 CoreFoundation`CFRunLoopRunSpecific + 452
    frame #36: 0x000000021aa8404c Foundation`-[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 304
    frame #37: 0x000000022e37c518 SceneKit`__49-[SCNView(SCNDisplayLink) _initializeDisplayLink]_block_invoke + 444
    frame #38: 0x000000022e37c684 SceneKit`__SCNRenderThread_start__ + 104
    frame #39: 0x0000000219d22908 libsystem_pthread.dylib`_pthread_body + 132
    frame #40: 0x0000000219d22864 libsystem_pthread.dylib`_pthread_start + 48
    frame #41: 0x0000000219d2adcc libsystem_pthread.dylib`thread_start + 4
warning: failed to set breakpoint site at 0x21931425c for breakpoint -4.1: error sending the breakpoint request
warning: failed to set breakpoint site at 0x219314530 for breakpoint -4.2: error sending the breakpoint request
warning: failed to set breakpoint site at 0x2193141b4 for breakpoint -4.3: error sending the breakpoint request
warning: failed to set breakpoint site at 0x21931fcc4 for breakpoint -5.1: error sending the breakpoint request

I'm not experienced in debugging in swift but it seems to me the problem occurs somewhere in frame #4-6 within some metal-framework code:

 frame #4: 0x000000021c28e23c Metal`MTLReportFailure + 528    
 frame #5: 0x000000023f984108 MetalTools`-[MTLDebugDevice validateNewBufferArgs:options:] + 172
 frame #6: 0x000000023f98430c MetalTools`-[MTLDebugDevice newBufferWithBytes:length:options:] + 128

Thankful for any help/suggestions!

---- EDIT 1 ----

Here is an array which does not generate this error:

vertices = [ //Array that should work
             SCNVector3(x: -0.06110339, y: -0.00659544, z: -0.18046863),
             SCNVector3(x: -0.06406027, y: -0.008907169, z: -0.18053372),
             SCNVector3(x: -0.06406027, y: -0.008907169, z: -0.18053372),
             SCNVector3(x: -0.06701318, y: -0.013257578, z: -0.18059872),
             SCNVector3(x: -0.069427274, y: -0.017816536, z: -0.18065183),
             SCNVector3(x: -0.07077661, y: -0.02299612, z: -0.18068156),
             SCNVector3(x: -0.07138735, y: -0.029295363, z: -0.18069498),
             SCNVector3(x: -0.07159121, y: -0.035330035, z: -0.1806995),
             SCNVector3(x: -0.06850778, y: -0.039139934, z: -0.1806316),
             SCNVector3(x: -0.059540674, y: -0.039537176, z: -0.18043421),
             SCNVector3(x: -0.04808737, y: -0.035914123, z: -0.1801821),
             SCNVector3(x: -0.045074403, y: -0.035180397, z: -0.1801158)
            ]

Here are two screenshots from my app when using this array. As you can see the polygon lies within the plane since all vertex positions are picked from a hit test on that plane. Don't mind the clipping colors since it's due to z-fighting and can easily be fixed by offsetting the planes:

good array 1 enter image description here

Here is an example of a vertices array which generates this error:

vertices = [ //Array that should not work
                SCNVector3(x: 0.08866002, y: -0.007735528, z: -0.09841499),
                SCNVector3(x: 0.08873053, y: -0.014926873, z: -0.09837532),
                SCNVector3(x: 0.08873053, y: -0.014926873, z: -0.09837532),
                SCNVector3(x: 0.08846086, y: -0.024348512, z: -0.09852711),
                SCNVector3(x: 0.08749959, y: -0.034751557, z: -0.09906833),
                SCNVector3(x: 0.08527064, y: -0.043312013, z: -0.10032329),
                SCNVector3(x: 0.08125973, y: -0.049623042, z: -0.10258152),
                SCNVector3(x: 0.07674095, y: -0.054563493, z: -0.10512567),
                SCNVector3(x: 0.07041831, y: -0.057908192, z: -0.10868551),
                SCNVector3(x: 0.06373097, y: -0.058204524, z: -0.112450644),
                SCNVector3(x: 0.058445737, y: -0.057790123, z: -0.115426354),
                SCNVector3(x: 0.054485526, y: -0.05334358, z: -0.11765605),
                SCNVector3(x: 0.052902386, y: -0.04610482, z: -0.1185474),
                SCNVector3(x: 0.053534307, y: -0.036374755, z: -0.118191615),
                SCNVector3(x: 0.055890974, y: -0.027881026, z: -0.11686475),
                SCNVector3(x: 0.059101492, y: -0.022751786, z: -0.115057185),
                SCNVector3(x: 0.062345386, y: -0.02150976, z: -0.113230795),
                SCNVector3(x: 0.06506948, y: -0.022176817, z: -0.11169703)
            ]

And here is a screenshot from when using that array. The blue planes are instances when the method worked, but if you look closely there is a red line. This red line is following the vertices of the bad array above, which crashes the app instantly when the polygon plane using the corresponding geometry is added to the scene:

Bad array

---- EDIT 2 ----

Below is the code for @ARGeo's macOS solution, but slightly modified to fit my iOS app and to use an arbitrary amount of vertices. The code works for the vertices array that previously failed but fails again when using another vertices array. Like @ARGeo proposed I made sure to place this method inside of the actual class and not in an extension in case MemoryLayout<Int32>.size is causing the issue when used inside an extension.

private func createPolygon(){

        func model(vertices: [SCNVector3]) -> SCNNode {

            let polyDraw = draw(vertices: vertices)

            let material = SCNMaterial()
            material.diffuse.contents = UIColor.green
            material.isDoubleSided = true

            material.diffuse.contentsTransform = .init(m11: 0.1, m12: 0,   m13: 0,  m14: 0,
                                                       m21: 0,   m22: 0.1, m23: 0,  m24: 0,
                                                       m31: 0,   m32: 0,   m33: 0,  m34: 0,
                                                       m41: 0,   m42: 0,   m43: 0,  m44: 1)

            material.diffuse.wrapS = .repeat
            material.diffuse.wrapT = .repeat
            polyDraw.materials = [material]
            let node = SCNNode(geometry: polyDraw)
            //node.scale =  SCNVector3(x: 200, y: 200, z: 200)
            sceneView.scene.rootNode.addChildNode(node)
            return node
        }

        func draw(vertices: [SCNVector3]) -> SCNGeometry {

            let normalsPerFace = 1

            var indices: [Int32] = []
            indices.append(Int32(vertices.count))

            //Add the rest of the indices 0 to vertices.count-1
            for i in 0 ... vertices.count-1 {
                indices.append(Int32(i))
            }

            let source = SCNGeometrySource(vertices: vertices)

            let vec = vertices.map { [SCNVector3](repeating: $0,
                                                                        count: normalsPerFace) }.flatMap{ $0 }
            let normals: [SCNVector3] = vec
            let normalSource = SCNGeometrySource(normals: normals)

            var cgps: [CGPoint] = []
            vertices.forEach { (vertex) in
                cgps.append(CGPoint(x: CGFloat(vertex.x), y: CGFloat(vertex.y)))
            }

            let textcoord = SCNGeometrySource(textureCoordinates: cgps)


            let data = Data(bytes: indices,
                            count: indices.count * MemoryLayout<Int32>.size)

            let element = SCNGeometryElement(data: data,
                                             primitiveType: .polygon,
                                             primitiveCount: 1,
                                             bytesPerIndex: MemoryLayout<Int32>.size)

            return SCNGeometry(sources: [source, normalSource, textcoord],                           elements: [element])
        }

        //Previous fail, now success with ARGeo's code
        let vertices = [SCNVector3(x: 0.08866002, y: -0.00773552, z: -0.09841499),
                    SCNVector3(x: 0.08873053, y: -0.01492687, z: -0.09837532),
                    SCNVector3(x: 0.08873053, y: -0.01492687, z: -0.09837532),
                    SCNVector3(x: 0.08846086, y: -0.02434851, z: -0.09852711),
                    SCNVector3(x: 0.08749959, y: -0.03475155, z: -0.09906833),
                    SCNVector3(x: 0.08527064, y: -0.04331201, z: -0.10032329),
                    SCNVector3(x: 0.08125973, y: -0.04962304, z: -0.10258152),
                    SCNVector3(x: 0.07674095, y: -0.05456349, z: -0.10512567),
                    SCNVector3(x: 0.07041831, y: -0.05790819, z: -0.10868551),
                    SCNVector3(x: 0.06373097, y: -0.05820452, z: -0.11245064),
                    SCNVector3(x: 0.05844573, y: -0.05779012, z: -0.11542635),
                    SCNVector3(x: 0.05448552, y: -0.05334358, z: -0.11765605),
                    SCNVector3(x: 0.05290238, y: -0.04610482, z: -0.11854740),
                    SCNVector3(x: 0.05353430, y: -0.03637475, z: -0.11819161),
                    SCNVector3(x: 0.05589097, y: -0.02788102, z: -0.11686475),
                    SCNVector3(x: 0.05910149, y: -0.02275178, z: -0.11505718),
                    SCNVector3(x: 0.06234538, y: -0.02150976, z: -0.11323079),
                    SCNVector3(x: 0.06506948, y: -0.02217681, z: -0.11169703)
                    ]

        _ = model(vertices: vertices)
}

Example of vertices array for which the code above fails:

vertices = [ SCNVector3(x: 0.08291423, y: -0.08406013, z: -0.60201955),
                     SCNVector3(x: 0.077855, y: -0.083336316, z: -0.60234916),
                     SCNVector3(x: 0.077855, y: -0.083336316, z: -0.60234916),
                     SCNVector3(x: 0.06817482, y: -0.08799789, z: -0.6029798),
                     SCNVector3(x: 0.055873748, y: -0.09737456, z: -0.6037812),
                     SCNVector3(x: 0.042388167, y: -0.108595274, z: -0.60465986),
                     SCNVector3(x: 0.031522393, y: -0.119523935, z: -0.6053677),
                     SCNVector3(x: 0.024102041, y: -0.13026507, z: -0.6058511),
                     SCNVector3(x: 0.021609604, y: -0.13820335, z: -0.6060136),
                     SCNVector3(x: 0.022751667, y: -0.14294992, z: -0.60593915),
                     SCNVector3(x: 0.025871918, y: -0.14491153, z: -0.6057359),
                     SCNVector3(x: 0.0338943, y: -0.14688163, z: -0.6052132),
                     SCNVector3(x: 0.041132875, y: -0.15027393, z: -0.60474163),
                     SCNVector3(x: 0.047307685, y: -0.15410759, z: -0.6043393),
                     SCNVector3(x: 0.054541387, y: -0.1566292, z: -0.603868),
                     SCNVector3(x: 0.06140149, y: -0.15919833, z: -0.60342115),
                     SCNVector3(x: 0.06551884, y: -0.16264887, z: -0.6031529)
                    ]

---- EDIT 3 ----

Okay so I made a macOS version for the code above (which is the solution @ARGeo proposed but with some minor changes, like using an arbitrary amount of vertices) which does not crash when using the above array, even though the iOS code version does. However nothing is rendered and I'm not getting any error messages which I don't know the reason for. When adding the line cameraNode.camera?.zFar = 1000 in case It's not visible due to clipping, the program crashes instead, giving a new error. It does not crash if zFar is small enough (tested with zFar < 10), in which case the polygon is not rendered either. The error print is the same -[MTLDebugDevice validateNewBufferArgs:options:]:467: failed assertion 'Cannot create buffer of zero length.' but the debug navigator shows this:

debug navigator

and the stack trace's final rows are this:

* thread #8, name = 'CVDisplayLink', queue = 'com.apple.scenekit.renderingQueue.SCNView0x101306480', stop reason = signal SIGABRT
  * frame #0: 0x00007fff671b62c6 libsystem_kernel.dylib`__pthread_kill + 10
    frame #1: 0x000000010053680d libsystem_pthread.dylib`pthread_kill + 284
    frame #2: 0x00007fff671206a6 libsystem_c.dylib`abort + 127
    frame #3: 0x00007fff670e920d libsystem_c.dylib`__assert_rtn + 324
    frame #4: 0x00007fff3fd1b68e Metal`MTLReportFailure + 567
    frame #5: 0x00007fff599f9d98 MetalTools`-[MTLDebugDevice validateNewBufferArgs:options:] + 207
    frame #6: 0x00007fff599f9f28 MetalTools`-[MTLDebugDevice newBufferWithBytes:length:options:] + 107
    frame #7: 0x00007fff462f265b SceneKit`-[SCNMTLResourceManager _bufferForData:bytesPerIndex:] + 414
    frame #8: 0x00007fff462f29e0

Here is the code which is used if you want to try it out yourself. Just copy and paste into macOS project:


import SceneKit
import QuartzCore

class GameViewController: NSViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let scene = SCNScene()

        let cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        scene.rootNode.addChildNode(cameraNode)
        cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)

        // This line crashes the app! But w/o it the polygon plane would probably clip and be invisible.
        cameraNode.camera?.zFar = 1000

        let ambientLightNode = SCNNode()
        ambientLightNode.light = SCNLight()
        ambientLightNode.light!.type = .ambient
        ambientLightNode.light!.intensity = 10000
        ambientLightNode.light!.color = NSColor.darkGray
        scene.rootNode.addChildNode(ambientLightNode)

        func model(vertices: [SCNVector3]) -> SCNNode {

            let polyDraw = draw(vertices: vertices)

            let material = SCNMaterial()
            material.diffuse.contents = NSColor.green
            material.isDoubleSided = true

            material.diffuse.contentsTransform = .init(m11: 0.1, m12: 0,   m13: 0,  m14: 0,
                                                       m21: 0,   m22: 0.1, m23: 0,  m24: 0,
                                                       m31: 0,   m32: 0,   m33: 0,  m34: 0,
                                                       m41: 0,   m42: 0,   m43: 0,  m44: 1)

            material.diffuse.wrapS = .repeat
            material.diffuse.wrapT = .repeat
            polyDraw.materials = [material]
            let node = SCNNode(geometry: polyDraw)
            node.scale =  SCNVector3(x: 200, y: 200, z: 200)
            scene.rootNode.addChildNode(node)
            return node
        }

        func draw(vertices: [SCNVector3]) -> SCNGeometry {

            let normalsPerFace = 1

            var indices: [Int32] = []
            indices.append(Int32(vertices.count))

            //Add the rest of the indices 0 to vertices.count-1
            for i in 0 ... vertices.count-1 {
                indices.append(Int32(i))
            }

            let source = SCNGeometrySource(vertices: vertices)

            let vec = vertices.map { [SCNVector3](repeating: $0,
                                                  count: normalsPerFace) }.flatMap{ $0 }
            let normals: [SCNVector3] = vec
            let normalSource = SCNGeometrySource(normals: normals)

            var cgps: [CGPoint] = []
            vertices.forEach { (vertex) in
                cgps.append(CGPoint(x: CGFloat(vertex.x), y: CGFloat(vertex.y)))
            }

            let textcoord = SCNGeometrySource(textureCoordinates: cgps)


            let data = Data(bytes: indices,
                            count: indices.count * MemoryLayout<Int32>.size)

            let element = SCNGeometryElement(data: data,
                                             primitiveType: .polygon,
                                             primitiveCount: 1,
                                             bytesPerIndex: MemoryLayout<Int32>.size)

            return SCNGeometry(sources: [source, normalSource, textcoord],                           elements: [element])
        }

        //Previous fail, now success with ARGeo's code
//        let vertices = [SCNVector3(x: 0.08866002, y: -0.00773552, z: -0.09841499),
//                        SCNVector3(x: 0.08873053, y: -0.01492687, z: -0.09837532),
//                        SCNVector3(x: 0.08873053, y: -0.01492687, z: -0.09837532),
//                        SCNVector3(x: 0.08846086, y: -0.02434851, z: -0.09852711),
//                        SCNVector3(x: 0.08749959, y: -0.03475155, z: -0.09906833),
//                        SCNVector3(x: 0.08527064, y: -0.04331201, z: -0.10032329),
//                        SCNVector3(x: 0.08125973, y: -0.04962304, z: -0.10258152),
//                        SCNVector3(x: 0.07674095, y: -0.05456349, z: -0.10512567),
//                        SCNVector3(x: 0.07041831, y: -0.05790819, z: -0.10868551),
//                        SCNVector3(x: 0.06373097, y: -0.05820452, z: -0.11245064),
//                        SCNVector3(x: 0.05844573, y: -0.05779012, z: -0.11542635),
//                        SCNVector3(x: 0.05448552, y: -0.05334358, z: -0.11765605),
//                        SCNVector3(x: 0.05290238, y: -0.04610482, z: -0.11854740),
//                        SCNVector3(x: 0.05353430, y: -0.03637475, z: -0.11819161),
//                        SCNVector3(x: 0.05589097, y: -0.02788102, z: -0.11686475),
//                        SCNVector3(x: 0.05910149, y: -0.02275178, z: -0.11505718),
//                        SCNVector3(x: 0.06234538, y: -0.02150976, z: -0.11323079),
//                        SCNVector3(x: 0.06506948, y: -0.02217681, z: -0.11169703)
//        ]

        //Array which fails when the cameras zFar property
        //is too big (>10 or something).
        //If zFar is small enough it does not crash,
        //but then nothing is rendered.
        let vertices = [ SCNVector3(x: 0.08291423, y: -0.08406013, z: -0.60201955),
                         SCNVector3(x: 0.077855, y: -0.083336316, z: -0.60234916),
                         SCNVector3(x: 0.077855, y: -0.083336316, z: -0.60234916),
                         SCNVector3(x: 0.06817482, y: -0.08799789, z: -0.6029798),
                         SCNVector3(x: 0.055873748, y: -0.09737456, z: -0.6037812),
                         SCNVector3(x: 0.042388167, y: -0.108595274, z: -0.60465986),
                         SCNVector3(x: 0.031522393, y: -0.119523935, z: -0.6053677),
                         SCNVector3(x: 0.024102041, y: -0.13026507, z: -0.6058511),
                         SCNVector3(x: 0.021609604, y: -0.13820335, z: -0.6060136),
                         SCNVector3(x: 0.022751667, y: -0.14294992, z: -0.60593915),
                         SCNVector3(x: 0.025871918, y: -0.14491153, z: -0.6057359),
                         SCNVector3(x: 0.0338943, y: -0.14688163, z: -0.6052132),
                         SCNVector3(x: 0.041132875, y: -0.15027393, z: -0.60474163),
                         SCNVector3(x: 0.047307685, y: -0.15410759, z: -0.6043393),
                         SCNVector3(x: 0.054541387, y: -0.1566292, z: -0.603868),
                         SCNVector3(x: 0.06140149, y: -0.15919833, z: -0.60342115),
                         SCNVector3(x: 0.06551884, y: -0.16264887, z: -0.6031529)
                        ]

        _ = model(vertices: vertices)


        let scnView = self.view as! SCNView
        scnView.scene = scene
        scnView.allowsCameraControl = true
        scnView.showsStatistics = true
        scnView.backgroundColor = NSColor.darkGray
    }
}

I have no idea what is causing this issue, as it seems to be some internal rendering going wrong. I'm also not very experienced with debugging so I would appreciate any help as I'm unable to make any further sense of this.

1
There are different coordinates and vertices every time the method is called as I’m allowing the user to “paint” an outline of the plane. For example a rectangle could have vertices = [topLeft, topRight, bottomRight, bottomLeft] where every vertex is a SCNVector3 corresponding to a position retrieved from a point in a SCNPlane. These kind of shapes seem to be working, but sometimes when the user is painting a “non-existing shape” such as vertices = [topLeft, bottomRight, bottomLeft, topRight] (where the order is wrong) I get this crash, especially when many more vertices are used.A. Claesson
This does not always happen when using such “non-existing shapes”, and from my testing it seems as the probability for a crash is much higher when more vertices are used.A. Claesson
Hmm, I'm surprised this ever works. Have you read the documentation related to the special rules for geometry elements when the primitive type is .polygon?warrenm
@warrenm yes, my method should create an indices array where the first element is the number of indices. Then I also use primitiveCount: 1 in the SCNGeometryElement function to account for these rules. Example: indices = [4, 0, 1, 2, 3] if there are 4 vertices. I'm following the answer to this threadA. Claesson
@A.Claesson Sorry for the noise; didn't realize you were initializing the array with the number of indices.warrenm

1 Answers

2
votes

SOLUTION (Copy-paste this macOS app code for testing in ViewController.swift):

enter image description here

import SceneKit

class ViewController: NSViewController, SCNSceneRendererDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        let scene = SCNScene()
        let cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        cameraNode.camera?.zFar = 1000
        scene.rootNode.addChildNode(cameraNode)
        cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)

        let scnView = self.view as! SCNView
        scnView.scene = scene
        scnView.delegate = self
        scnView.allowsCameraControl = true
        scnView.showsStatistics = true
        scnView.backgroundColor = NSColor.darkGray

        func model( v01: SCNVector3,
                    v02: SCNVector3,
                    v03: SCNVector3,
                    v04: SCNVector3,
                    v05: SCNVector3,
                    v06: SCNVector3,
                    v07: SCNVector3,
                    v08: SCNVector3,
                    v09: SCNVector3,
                    v10: SCNVector3,
                    v11: SCNVector3,
                    v12: SCNVector3,
                    v13: SCNVector3,
                    v14: SCNVector3,
                    v15: SCNVector3,
                    v16: SCNVector3,
                    v17: SCNVector3,
                    v18: SCNVector3) -> SCNNode {

            let polyDraw = draw(vector01: v01,
                                vector02: v02,
                                vector03: v03,
                                vector04: v04,
                                vector05: v05,
                                vector06: v06,
                                vector07: v07,
                                vector08: v08,
                                vector09: v09,
                                vector10: v10,
                                vector11: v11,
                                vector12: v12,
                                vector13: v13,
                                vector14: v14,
                                vector15: v15,
                                vector16: v16,
                                vector17: v17,
                                vector18: v18)

            let material = SCNMaterial()
            material.diffuse.contents = NSColor.green
            material.isDoubleSided = true
            polyDraw.materials = [material]

            let node = SCNNode(geometry: polyDraw)
            node.scale =  SCNVector3(x: 200, y: 200, z: 200)
            scene.rootNode.addChildNode(node)
            return node
        }

        func draw(vector01: SCNVector3,
                  vector02: SCNVector3,
                  vector03: SCNVector3,
                  vector04: SCNVector3,
                  vector05: SCNVector3,
                  vector06: SCNVector3,
                  vector07: SCNVector3,
                  vector08: SCNVector3,
                  vector09: SCNVector3,
                  vector10: SCNVector3,
                  vector11: SCNVector3,
                  vector12: SCNVector3,
                  vector13: SCNVector3,
                  vector14: SCNVector3,
                  vector15: SCNVector3,
                  vector16: SCNVector3,
                  vector17: SCNVector3,
                  vector18: SCNVector3) -> SCNGeometry {

            let normalsPerFace = 1

            let indices: [Int32] = [18, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
                                        10, 11, 12, 13, 14, 15, 16, 17]

            let source = SCNGeometrySource(vertices: [vector01,
                                                      vector02,
                                                      vector03,
                                                      vector04,
                                                      vector05,
                                                      vector06,
                                                      vector07,
                                                      vector08,
                                                      vector09,
                                                      vector10,
                                                      vector11,
                                                      vector12,
                                                      vector13,
                                                      vector14,
                                                      vector15,
                                                      vector16,
                                                      vector17,
                                                      vector18])

            let vec = [vector01, vector02, vector03,
                       vector04, vector05, vector06,
                       vector07, vector08, vector09,
                       vector10, vector11, vector12,
                       vector13, vector14, vector15,
                       vector16, vector17, vector18].map { [SCNVector3](repeating: $0,
                                                                            count: normalsPerFace) }.flatMap{ $0 }
            let normals: [SCNVector3] = vec
            let normalSource = SCNGeometrySource(normals: normals)

            let point01 = CGPoint(x: CGFloat(vector01.x), y: CGFloat(vector01.y))
            let point02 = CGPoint(x: CGFloat(vector02.x), y: CGFloat(vector02.y))
            let point03 = CGPoint(x: CGFloat(vector03.x), y: CGFloat(vector03.y))
            let point04 = CGPoint(x: CGFloat(vector04.x), y: CGFloat(vector04.y))
            let point05 = CGPoint(x: CGFloat(vector05.x), y: CGFloat(vector05.y))
            let point06 = CGPoint(x: CGFloat(vector06.x), y: CGFloat(vector06.y))
            let point07 = CGPoint(x: CGFloat(vector07.x), y: CGFloat(vector07.y))
            let point08 = CGPoint(x: CGFloat(vector08.x), y: CGFloat(vector08.y))
            let point09 = CGPoint(x: CGFloat(vector09.x), y: CGFloat(vector09.y))
            let point10 = CGPoint(x: CGFloat(vector10.x), y: CGFloat(vector10.y))
            let point11 = CGPoint(x: CGFloat(vector11.x), y: CGFloat(vector11.y))
            let point12 = CGPoint(x: CGFloat(vector12.x), y: CGFloat(vector12.y))
            let point13 = CGPoint(x: CGFloat(vector13.x), y: CGFloat(vector13.y))
            let point14 = CGPoint(x: CGFloat(vector14.x), y: CGFloat(vector14.y))
            let point15 = CGPoint(x: CGFloat(vector15.x), y: CGFloat(vector15.y))
            let point16 = CGPoint(x: CGFloat(vector16.x), y: CGFloat(vector16.y))
            let point17 = CGPoint(x: CGFloat(vector17.x), y: CGFloat(vector17.y))
            let point18 = CGPoint(x: CGFloat(vector18.x), y: CGFloat(vector18.y))

            let texCoord = SCNGeometrySource(textureCoordinates:
                           [point01, point02, point03, point04, point05, point06,
                            point07, point08, point09, point10, point11, point12,
                            point13, point14, point15, point16, point17, point18])

            let data = Data(bytes: indices,
                            count: indices.count * MemoryLayout<Int32>.size)

            let element = SCNGeometryElement(data: data,
                                    primitiveType: .polygon,
                                   primitiveCount: 1,
                                    bytesPerIndex: MemoryLayout<Int32>.size)

            let geometry = SCNGeometry(sources: [source, normalSource, texCoord],
                                      elements: [element])

            return geometry
        }

        _ = model(v01: SCNVector3(x: 0.08866002, y: -0.00773552, z: -0.09841499),
                  v02: SCNVector3(x: 0.08873053, y: -0.01492687, z: -0.09837532),
                  v03: SCNVector3(x: 0.08873053, y: -0.01492687, z: -0.09837532),
                  v04: SCNVector3(x: 0.08846086, y: -0.02434851, z: -0.09852711),
                  v05: SCNVector3(x: 0.08749959, y: -0.03475155, z: -0.09906833),
                  v06: SCNVector3(x: 0.08527064, y: -0.04331201, z: -0.10032329),
                  v07: SCNVector3(x: 0.08125973, y: -0.04962304, z: -0.10258152),
                  v08: SCNVector3(x: 0.07674095, y: -0.05456349, z: -0.10512567),
                  v09: SCNVector3(x: 0.07041831, y: -0.05790819, z: -0.10868551),
                  v10: SCNVector3(x: 0.06373097, y: -0.05820452, z: -0.11245064),
                  v11: SCNVector3(x: 0.05844573, y: -0.05779012, z: -0.11542635),
                  v12: SCNVector3(x: 0.05448552, y: -0.05334358, z: -0.11765605),
                  v13: SCNVector3(x: 0.05290238, y: -0.04610482, z: -0.11854740),
                  v14: SCNVector3(x: 0.05353430, y: -0.03637475, z: -0.11819161),
                  v15: SCNVector3(x: 0.05589097, y: -0.02788102, z: -0.11686475),
                  v16: SCNVector3(x: 0.05910149, y: -0.02275178, z: -0.11505718),
                  v17: SCNVector3(x: 0.06234538, y: -0.02150976, z: -0.11323079),
                  v18: SCNVector3(x: 0.06506948, y: -0.02217681, z: -0.11169703))
    }
}

USEFUL INFO:

In 3D graphics the best and most predictable way to work with polygonal geometry is to initially use three-sided (triangles) and four-sided (quadrangles) faces. Sometimes, in rare cases, you can use five-sided faces but this can lead you to shading artefacts.

Bad cases that can potentially lead to errors in SceneKit/Metal are:

  • Lamina Faces
  • Non-Manifold Geometry
  • Non-planar Faces (your case)
  • Concave Faces
  • "Turned inside out" Faces i.e. wrong connection's order (your case)
  • Faces with Holes
  • Faces with Edges that have a zero length
  • etc...

And one more important thing I should say is: at rendering stage all polygons always turn into triangles. If renderer or rendering engine can't fulfil this transformation you'll get errors.

Look how four-sided polygons are competently connected to form a complex object:

enter image description here

P.S. camera.zFar for ARKit.

let currentFrame = sceneView.session.currentFrame
let node = SCNNode()
node.camera = SCNCamera()

var translation = matrix_identity_float4x4

translation.columns.3.z = -0.1       /* 10 cm */
node.simdTransform = matrix_multiply((currentFrame?.camera.transform)!, 
                                      translation)

node.camera?.zFar = 1000             /* Set no more than 1000 meters */