I am keen to adopt the new Swift language as this seems to be the way forward with Apple development. I have also been impressed with the new SceneKit support in iOS 8. I wo
Well, the two pieces of code doesn't translate exactly to one another. The int
in C is not the same as Int
in Swift. It's actually called CInt
in Swift:
/// The C 'int' type.
typealias CInt = Int32
If you change both occurrences to use CInt
instead, the error message that you previously got goes away (at least for me in an OS X Playground. However, it still doesn't render anything for me.
I don't think sizeofValue
is used to return the size of an array. It looks to me like it's returning the size of the pointer:
let indexes: CInt[] = [0, 1, 2]
sizeofValue(indexes) // is 8
sizeof(CInt) // is 4
sizeof(CInt) * countElements(indexes) // is 12
// compare to other CInt[]
let empty: CInt[] = []
let large: CInt[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
sizeofValue(indexes) // is 8 (your array of indices again)
sizeofValue(empty) // is 8
sizeofValue(large) // is 8
So, for me the following code works (I've put the arguments on different lines to make it easier to point out my changes):
let src = SCNGeometrySource(vertices: &verts, count: 3)
let indexes: CInt[] = [0, 1, 2] // Changed to CInt
let dat = NSData(
bytes: indexes,
length: sizeof(CInt) * countElements(indexes) // Changed to size of CInt * count
)
let ele = SCNGeometryElement(
data: dat,
primitiveType: .Triangles,
primitiveCount: 1,
bytesPerIndex: sizeof(CInt) // Changed to CInt
)
let geo = SCNGeometry(sources: [src], elements: [ele])
let nd = SCNNode(geometry: geo)
scene.rootNode.addChildNode(nd)
With this result:
I don't know a lot about swift but I would expect your versions of "verts" and "indexes" to be completely different in swift and ObjC (actually C).
In your C version you get a C array of C structures. In swift I assume you get a swift "array" (equivalent to NSArray) of SCNVector3 wrapped into NSValues. so the NSData you build with the "indexes" bytes doesn't contain a flat buffer of float values like in C. Same for the "verts" array.
Looks like such C and swift interoperability issues are discussed here: Swift use c struct
David's answer is pretty good, but to present a more complete picture...
A Swift array of structs packs its data together like a C array of structs, not an ObjC NSArray
of NSValue
-wrapped structs. And you can pass a Swift array to (Obj)C APIs that take a CMutablePointer or similar, so it's totally cool to construct an NSData
from a Swift array.
The same goes for arrays of / structs of structs, as long as they all get down to scalar types in the end.
Even in ObjC, making an array of SCNVector3
can be problematic, because the element type of that struct changes between 32/64-bit architectures.
sizeOfValue
doesn't seem to be working as expected for arrays in Beta 2, so I'd recommend filing a bug.
If you implement the ArrayLiteralConvertible
protocol in your custom structs, you can declare arrays of nested struct values concisely.
If you're aware of these issues, you can use in Swift, with some variation, most of the same tricks for vertex/index buffer management that you would in (Obj)C. For example, here's a snippet that builds a custom geometry with interleaved vertex and normal data (which is good for performance on iOS device GPUs):
struct Float3 : ArrayLiteralConvertible {
typealias Element = GLfloat
var x, y, z: GLfloat
init(arrayLiteral elements: Element...) {
self.x = elements[0]
self.y = elements[1]
self.z = elements[2]
}
}
struct Vertex: ArrayLiteralConvertible {
typealias Element = Float3
var position, normal: Float3
init(arrayLiteral elements: Element...) {
self.position = elements[0]
self.normal = elements[1]
}
}
// This must be a var, not a let, because it gets passed to a CMutablePointer
var vertices: Vertex[] = [
[ [+0.5, -0.5, -0.5], [+1.0, +0.0, +0.0] ],
[ [+0.5, +0.5, -0.5], [+1.0, +0.0, +0.0] ],
// ... lots more vertices here!
]
let data = NSData(bytes: vertices, length: vertices.count * sizeof(Vertex))
let vertexSource = SCNGeometrySource(data: data,
semantic: SCNGeometrySourceSemanticVertex,
vectorCount: vertices.count,
floatComponents: true,
componentsPerVector: 3,
bytesPerComponent: sizeof(GLfloat),
dataOffset: 0,
dataStride: sizeof(Vertex))
let normalSource = SCNGeometrySource(data: data,
semantic: SCNGeometrySourceSemanticNormal,
vectorCount: vertices.count,
floatComponents: true,
componentsPerVector: 3,
bytesPerComponent: sizeof(GLfloat),
// no offsetof() in Swift, but Vertex has one Float3 before normal
dataOffset: sizeof(Float3),
dataStride: sizeof(Vertex))
// use a Swift Range to quickly construct a sequential index buffer
let indexData = NSData(bytes: Array<UInt8>(0..<UInt8(vertices.count)),
length: vertices.count * sizeof(UInt8))
let element = SCNGeometryElement(data: indexData,
primitiveType: .Triangles,
primitiveCount: vertices.count / 3,
bytesPerIndex: sizeof(UInt8))
let cube = SCNGeometry(sources: [vertexSource, normalSource], elements: [element])