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:
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:
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:
---- 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:
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.
Solution
Copy-paste this macOS app code for testing in ViewController.swift:
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:
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 */