iOS中的Swift中的自定义SceneKit几何不工作,但等价的目标C代码

我一直使用这个网站,但这是我的第一个问题。

我渴望采用新的Swift语言,因为这似乎是苹果公司发展的方向。 我也对iOS 8中的新SceneKit支持留下了深刻的印象。我想在运行时以编程方式创build自定义几何体,但是我正在努力使Swift代码正常工作。 然而Objective C中的等价代码工作正常。

这可能是一个错误,或者我做错了什么。

我只是试图创build和呈现一个单一的三angular形。 为了简单起见,我将忽略法线和纹理等。 所以我只希望看到一个黑色的三angular形。

Swift代码(不工作)

var verts = [SCNVector3(x: 0,y: 0,z: 0),SCNVector3(x: 1,y: 0,z: 0),SCNVector3(x: 0,y: 1,z: 0)] let src = SCNGeometrySource(vertices: &verts, count: 3) let indexes:Int[]=[0,1,2] let dat = NSData(bytes: indexes, length: sizeofValue(indexes)) let ele = SCNGeometryElement(data:dat, primitiveType: .Triangles, primitiveCount: 1, bytesPerIndex: sizeof(Int)) let geo = SCNGeometry(sources: [src], elements: [ele]) let nd = SCNNode(geometry: geo) scene.rootNode.addChildNode(nd) 

目标C代码(它工作)

 SCNVector3 verts[] = { SCNVector3Make(0, 0, 0), SCNVector3Make(1, 0, 0), SCNVector3Make(0, 1, 0) }; SCNGeometrySource *src = [SCNGeometrySource geometrySourceWithVertices:verts count:3]; int indexes[] = { 0, 1, 2 }; NSData *datIndexes = [NSData dataWithBytes:indexes length:sizeof(indexes)]; SCNGeometryElement *ele = [SCNGeometryElement geometryElementWithData:datIndexes primitiveType:SCNGeometryPrimitiveTypeTriangles primitiveCount:1 bytesPerIndex:sizeof(int)]; SCNGeometry *geo = [SCNGeometry geometryWithSources:@[src] elements:@[ele]]; SCNNode *nd = [SCNNode nodeWithGeometry:geo]; d [scene.rootNode addChildNode:nd]; 

任何指针(赦免双关语)将不胜感激。

谢谢

那么,这两个代码不会完全相互转换。 C中的Int与Swift中的Int 。 它实际上在Swift中被称为CInt

 /// The C 'int' type. typealias CInt = Int32 

如果你改变这两个事件来使用CInt来代替,你以前得到的错误信息就会消失(至less对于我来说,在OS X Playground中是这样,但是它仍然不会为我提供任何东西。

我不认为sizeofValue是用来返回一个数组的大小。 它看起来像它正在返回指针的大小:

 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 

所以,对于我来说,下面的代码工作(我把这些参数放在不同的行上,以便更容易指出我的更改):

 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) 

有了这个结果:

在这里输入图像说明

大卫的答案相当不错,但要提出一个更完整的画面…

  • 一个Swift数组结构将数据打包成一个C数组,而不是一个由NSValue包装的结构体构成的ObjC NSArray 。 你可以将一个Swift数组传递给(Obj)C API,这个API需要一个CMutablePointer或类似的东西 ,所以从Swift数组中构造一个NSData是非常酷的。

  • 对于结构体/结构体的数组也是如此,只要它们最终都归结为标量types。

  • 即使在ObjC中,创build一个SCNVector3数组也是有问题的,因为该结构的元素types在32/64位体系结构之间变化。

  • 在Beta 2中, sizeOfValue似乎不像预期的那样工作,所以我build议提交一个bug 。

  • 如果您在自定义结构中实现了ArrayLiteralConvertible协议,则可以简洁地声明嵌套结构值的数组。

如果你意识到了这些问题,你可以在Swift中使用,有一些变化,你可以在(Obj)C中使用顶点/索引缓冲区pipe理的大部分技巧。 例如,下面的代码片断使用交错的顶点数据和正常的数据构build自定义几何graphics(这对于iOS设备GPU上的性能很有帮助):

 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]) 

我不知道很多关于swift的内容,但我希望你的版本“verts”和“indexes”在swift和ObjC(实际上是C)中是完全不同的。

在你的C版本中,你得到一个C结构的C数组。 在swift中,我假设你得到一个包含NSValues的SCNVector3的快速“数组”(相当于NSArray)。 所以用“索引”字节构build的NSData不包含像C中一样的float值的平坦缓冲区。对于“verts”数组也是如此。

看起来像这样的C和Swift互操作性问题在这里讨论: Swift使用C结构