I should see 2 yellow triangles, but I see nothing.
class Terrain {
private class func createGeometry () -> SCNGeometry {
let sources = [
SCNGeometrySource(vertices:[
SCNVector3(x: -1.0, y: -1.0, z: 0.0),
SCNVector3(x: -1.0, y: 1.0, z: 0.0),
SCNVector3(x: 1.0, y: 1.0, z: 0.0),
SCNVector3(x: 1.0, y: -1.0, z: 0.0)], count:4),
SCNGeometrySource(normals:[
SCNVector3(x: 0.0, y: 0.0, z: -1.0),
SCNVector3(x: 0.0, y: 0.0, z: -1.0),
SCNVector3(x: 0.0, y: 0.0, z: -1.0),
SCNVector3(x: 0.0, y: 0.0, z: -1.0)], count:4)
]
let elements = [
SCNGeometryElement(indices: [0, 2, 3, 0, 1, 2], primitiveType: .Triangles)
]
let geo = SCNGeometry(sources:sources, elements:elements)
let mat = SCNMaterial()
mat.diffuse.contents = UIColor.yellowColor()
mat.doubleSided = true
geo.materials = [mat]
return geo
}
class func createNode () -> SCNNode {
let node = SCNNode(geometry: createGeometry())
node.name = "Terrain"
node.position = SCNVector3()
return node
}
}
I use it as follows:
let terrain = Terrain.createNode()
sceneView.scene?.rootNode.addChildNode(terrain)
let camera = SCNCamera()
camera.zFar = 10000
self.camera = SCNNode()
self.camera.camera = camera
self.camera.position = SCNVector3(x: -20, y: 15, z: 30)
let constraint = SCNLookAtConstraint(target: terrain)
constraint.gimbalLockEnabled = true
self.camera.constraints = [constraint]
sceneView.scene?.rootNode.addChildNode(self.camera)
I get other nodes with non-custom geometry which I see. What's wrong?
Note: see Ash's answer, which is a much better approach for modern Swift than this one.
Your index array has the wrong size element. It's being inferred as [Int]
. You need [CInt]
.
I broke out your elements
setup into:
let indices = [0, 2, 3, 0, 1, 2] // [Int]
print(sizeof(Int)) // 8
print(sizeof(CInt)) // 4
let elements = [
SCNGeometryElement(indices: indices, primitiveType: .Triangles)
]
To get the indices to be packed like the expected C array, declare the type explicitly:
let indices: [CInt] = [0, 2, 3, 0, 1, 2]
Custom SceneKit Geometry in Swift on iOS not working but equivalent Objective C code does goes into more detail, but it's written against Swift 1, so you'll have to do some translation.
SCNGeometryElement(indices:, primitiveType:)
doesn't appear to be documented anywhere, although it does appear in the headers.
Hal Mueller is quite correct in that the indices involved must be a specified type, but it should be noted that this functionality has changed significantly in recent versions of the Swift language. Notably, SCNGeometryElement(indices:, primitiveType:)
now functions perfectly well in Swift 4 and I would advise against using CInt
which did not work for me. Instead use one of the standard integer types that conforms to the FixedWidthInteger
protocol, i.e. Int32
. If you know there's a maximum number of vertices involved in your mesh, use the smallest bit size you can that will encompass all of them.
Example:
let vertices = [
SCNVector3(x: 5, y: 4, z: 0),
SCNVector3(x: -5 , y: 4, z: 0),
SCNVector3(x: -5, y: -5, z: 0),
SCNVector3(x: 5, y: -5, z: 0)
]
let allPrimitives: [Int32] = [0, 1, 2, 0, 2, 3]
let vertexSource = SCNGeometrySource(vertices: vertices)
let element = SCNGeometryElement(indices: allPrimitives, primitiveType: .triangles)
let geometry = SCNGeometry(sources: [vertexSource], elements: [element])
SCNNode(geometry: geometry)
What's Happening Here?
First we create an array of vertices
describing points in three-dimensional space. The allPrimitives
array describes how those vertices link up. Each element is an index from the vertices
array. Since we're using triangles, these should be considered in groups of three, one for each corner. For simplicity's sake I've done a simple flat square here. We then create a geometry source with the semantic type of vertices
using the original array of all vertices, and a geometry element using the allPrimitives
array, also informing it that they are triangles so it knows to group them in threes. These can then be used to create the SCNGeometry
object with which we initialise our SCNNode
.
An easy way to think about it is that the vertex source exists only to list all the vertices in the object. The geometry element exists only to describe how those vertices are linked up. It's the SCNGeometry
that combines these two objects together to create the final physical representation.