swift SceneKit Sphere deformation after changing position - swift

I have two spheres on the sceneView
let earthSphere = SCNSphere(radius: 5.0)
earthSphere.firstMaterial?.diffuse.contents = UIColor.green
let objNode1 = SCNNode(geometry: earthSphere)
objNode1.position = SCNVector3(x: 0, y: 0, z: 0)
let moonSphere = SCNSphere(radius: 0.5)
moonSphere.firstMaterial?.diffuse.contents = UIColor.red
let objNode2 = SCNNode(geometry: moonSphere)
objNode2.position = SCNVector3(x: 3, y: 3, z: 0)
but moonSphere is deforming image. How to fix that ? It is possible to do that earthSphere and moonSphere look same ? without deformation
etc: cameraNode set to position SCNVector3(x: 0, y: 0, z: 7)

Related

Creating a custom SCNGeometry polygon plane with SCNGeometryPrimitiveType polygon crash/error

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 */

Add shadow to SCNPlane

I have ARKit artwork app. And I want my artworks to have a shadow just behind it. The shadow mustn't be dynamic. Just static shadow.
I've tried to add a plane behind the artwork. And set a shadow to it. But I discovered that it is not easy.
Can you help me?
let shadow = SCNPlane(width: artwork.size.width * 0.0254 + 0.03, height: artwork.size.height * 0.0254 + 0.015)
let layer = CALayer()
layer.frame = CGRect(origin: .zero, size: CGSize(width: shadow.width, height: shadow.height))
layer.shadowColor = UIColor.black.cgColor
layer.shadowOpacity = 1
layer.shadowOffset = .zero
layer.shadowRadius = 10
shadow.firstMaterial?.diffuse.contents = layer
The shadow should be like in UIKit's UIView. But this code just doesn't work.
I need something like
UPDATED.
In order to cast and receive a shadows from 3D lights, you have to use a 3D primitives (like cube, sphere, cylinder, etc), or 3D models in such formats as .usdz, .dae or .obj.
Shadow on a visible plane.
Use the following code to test how light generates shadows for 3D geometry:
let scene = SCNScene()
let lightNode = SCNNode()
lightNode.light = SCNLight()
lightNode.light!.type = .spot
lightNode.light!.castsShadow = true
lightNode.light!.shadowMode = .deferred
lightNode.rotation = SCNVector4(x: -1, y: 0, z: 0, w: CGFloat.pi/2)
lightNode.position = SCNVector3(x: 0, y: 20, z: 0)
scene.rootNode.addChildNode(lightNode)
let sphereNode = SCNNode()
sphereNode.geometry = SCNSphere(radius: 2)
sphereNode.position = SCNVector3(x: 0, y: -2, z: 0)
scene.rootNode.addChildNode(sphereNode)
let planeNode = SCNNode()
planeNode.geometry = SCNPlane(width: 15, height: 15)
planeNode.position = SCNVector3(x: 0, y: -5, z: 0)
planeNode.rotation = SCNVector4(x: -1, y: 0, z: 0, w: CGFloat.pi/2)
planeNode.geometry?.materials.first?.diffuse.contents = UIColor.red
scene.rootNode.addChildNode(planeNode)
Shadow on invisible plane.
In case you need an invisible plane that receives a shadow, use the following code:
planeNode.geometry?.materials.first?.colorBufferWriteMask = []
planeNode.geometry?.materials.first?.writesToDepthBuffer = true
planeNode.geometry?.materials.first?.lightingModel = .constant
Fake shadow.
And if you want to use a fake shadow written as a .png (png file format can hold 4 channels) texture on geometry (with a premultiplied alpha channel – RGB x A), use the following approach:
planeNode.geometry?.materials.first?.diffuse.contents = UIImage(named: "shadow.png")
lightNode.light!.castsShadow = false
Here's my answer on fake shadows.
And here's your premultiplied shadow in .png format (just drag-and-drop it on your desktop):
You can change its size and transparency and, of course, you can blur it.

SCNVector4: Rotate on x-axis

I have a sphere and managed to rotate it. But unfortunately along the wrong axis.
My goal is a rotation like the earth along the x axis. Can you help me to apply this?
Here is my existing code:
let spin = CABasicAnimation(keyPath: "rotation")
// Use from-to to explicitly make a full rotation around z
spin.fromValue = NSValue(scnVector4: SCNVector4(x: 1, y: 0, z: 0, w: 0))
spin.toValue = NSValue(scnVector4: SCNVector4(x: 1, y: 0, z: 0, w: Float(CGFloat(-2 * Double.pi))))
spin.duration = 30
spin.repeatCount = .infinity
sphereNode.addAnimation(spin, forKey: "spin around")
It will be something like this
self.imageView.layer.transform = CATransform3DConcat(self.imageView.layer.transform, CATransform3DMakeRotation(M_PI,1.0,0.0,0.0));
You're already have working solution:
spin.fromValue = NSValue(scnVector4: SCNVector4(x: 1, y: 0, z: 0, w: 0))
spin.toValue = NSValue(scnVector4: SCNVector4(x: 1, y: 0, z: 0, w: CGFloat.pi * 2))
If you wanna change direction, just change vectors :)
spin.fromValue = NSValue(scnVector4: SCNVector4(x: 1, y: 0, z: 0, w: CGFloat.pi * 2))
spin.toValue = NSValue(scnVector4: SCNVector4(x: 1, y: 0, z: 0, w: 0))
You can rotate your Earth model about X-axis using SceneKit's Transaction:
let scene = SCNScene(named: "art.scnassets/model.scn")!
let modelEarth = scene.rootNode.childNode(withName: "model",
recursively: true)!
SCNTransaction.begin()
SCNTransaction.animationDuration = 500 // animation in seconds
SCNTransaction.animationTimingFunction = .init(name: .default)
modelEarth.rotation.x = 1
modelEarth.rotation.y = 0
modelEarth.rotation.z = 0
modelEarth.rotation.w = 100 * Float.pi // fourth component
SCNTransaction.commit()

simd_float4x4 Columns

I want to translate a plane without rotating the image. For any reason my image is being rotated.
var translation = matrix_identity_float4x4
translation.colum = -0.2
let transform = simd_mul(currentFrame.camera.transform, translation)
planeNode.simdWorldTransform = matrix_multiply(currentFrame.camera.transform, translation)
Also, I notice that matrix_identity_float4x4 contains 4 columns but the documentation is not available.
Why 4 columns? Are there the frame of the plane?
The simplest way to do it is to use the following code for positioning:
let planeNode = SCNNode()
planeNode.geometry = SCNPlane(width: 20, height: 20)
// At first we need to rotate a plane about its x axis in radians:
planeNode.rotation = SCNVector4(1, 0, 0, -Double.pi/2)
planeNode.geometry?.materials.first?.diffuse.contents = UIColor.red
planeNode.position.x = 10
planeNode.position.z = 10
// planeNode.position = SCNVector3(x: 10, y: 0, z: 10)
scene.rootNode.addChildNode(planeNode)
or this way:
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)
scene.rootNode.addChildNode(cameraNode)
let planeNode = SCNNode()
planeNode.geometry = SCNPlane(width: 20, height: 20)
planeNode.rotation = SCNVector4(1, 0, 0, -Double.pi/2)
planeNode.geometry?.materials.first?.diffuse.contents = UIColor.red
let distance: Float = 50
planeNode.simdPosition = cameraNode.simdWorldFront * distance // -Z axis
planeNode.simdPosition = cameraNode.simdWorldRight * distance // +X axis
scene.rootNode.addChildNode(planeNode)
If you wanna know more about matrices used in ARKit and SceneKit frameworks just look at Figure 1-8 Matrix configurations for common transformations.
Hope this helps.

SceneKit Swift - PhysicsBody not precise

Hello,
I'm making a game with SceneKit in Swift.
In this game I have first cube1 that is on a second cube2 (that is the ground).
PROBLEM 1
The cube1's PhysicsBodyType is Kinematic, while the second is Static.
I used Kinematic for the first cube because when I use Dynamic, the PhysicsBody is not precise as you can see on the image :
While when I use Kinematic, the PhysicsBody is right :
Do you know why the PhysicsBody is not precise ?
PROBLEM 2
I want to apply vertical force (y) to the cube1 but it is impossible to do with Kinematic type, so I tried to use SCNAction.moveBy but the movement is not smooth.
Have you a solution for this ?
CODE
CUBE 1 :
let cubeGeometry = SCNBox(width: cubeSize, height: cubeSize, length: cubeSize, chamferRadius: 0.0)
cubeGeometry.firstMaterial?.diffuse.contents = UIColor(netHex: 0xF1C40F)
cubeNode = SCNNode(geometry: cubeGeometry)
cubeNode.position = SCNVector3(x: 0.0, y: 0.0, z: 0.0)
let cubeShape = SCNPhysicsShape(geometry: cubeGeometry, options: nil)
cubeNode.physicsBody = SCNPhysicsBody(type: SCNPhysicsBodyType.Dynamic, shape: cubeShape)
cubeNode.physicsBody?.angularVelocityFactor = SCNVector3(0.0, 1.0, 0.0)
scene.rootNode.addChildNode(cubeNode)
CUBE 2 :
for index in 0...10 {
let cubeGeometry = SCNBox(width: cubeSize, height: cubeSize, length: cubeSize, chamferRadius: 0.0)
let randomColor = Int(arc4random_uniform(UInt32(colorsArray.count)))
cubeGeometry.firstMaterial?.diffuse.contents = colorsArray[randomColor]
let cubeGround = SCNNode(geometry: cubeGeometry)
cubeGround.position = SCNVector3(x: 0.0, y: -Float(cubeSize), z: Float(index) * -Float(cubeSize))
cubeGround.physicsBody = SCNPhysicsBody(type: SCNPhysicsBodyType.Static, shape: SCNPhysicsShape(node: cubeGround, options: nil))
cubeGround.physicsBody?.angularVelocityFactor = SCNVector3(0.0, 0.0, 0.0)
scene.rootNode.addChildNode(cubeGround)
}