xcode iOS 上的 Swift 中的自定义 SceneKit Geometry 不起作用,但等效的 Objective C 代码可以
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24493273/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Custom SceneKit Geometry in Swift on iOS not working but equivalent Objective C code does
提问by BassetMan
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 would like to programatically create custom geometry at runtime but I am struggling to get the Swift code to work. Yet the equivalent code in Objective C works OK.
我热衷于采用新的 Swift 语言,因为这似乎是 Apple 开发的前进方向。iOS 8 中新的 SceneKit 支持也给我留下了深刻的印象。我想在运行时以编程方式创建自定义几何体,但我正在努力让 Swift 代码工作。然而,Objective C 中的等效代码可以正常工作。
This could be a bug, or something I am doing wrong.
这可能是一个错误,或者我做错了什么。
I am simply trying to create and render a single triangle. I will ignore the normals and textures etc at this point for simplicity. So I am only expecting to see a black triangle.
我只是想创建和渲染一个三角形。为简单起见,此时我将忽略法线和纹理等。所以我只期待看到一个黑色的三角形。
Swift Code (not working)
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)
Objective C Code (which does work)
目标 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];
Any pointers would be appreciated.
任何指针将不胜感激。
采纳答案by David R?nnqvist
Well, the two pieces of code doesn't translate exactlyto one another. The int
in C is not the same as Int
in Swift. It's actually called CInt
in Swift:
好吧,这两段代码并不能完全相互转换。该int
在C是不一样Int
的斯威夫特。它实际上是CInt
在 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.
如果您将两个事件都改为使用CInt
,您之前收到的错误消息就会消失(至少对我在 OS X Playground 中是这样。但是,它仍然没有为我呈现任何内容。
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:
我不认为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
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:
有了这个结果:
回答by rickster
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
ofNSValue
-wrapped structs. And you can pass a Swift array to (Obj)C APIs that take aCMutablePointer
or similar, so it's totally cool to construct anNSData
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.
Swift 结构数组将其数据打包在一起,就像 C 结构数组一样,而不是封装结构
NSArray
的NSValue
ObjC。并且您可以将 Swift 数组传递给采用 aCMutablePointer
或类似 的(Obj)C API,因此NSData
从 Swift 数组构造 an 非常酷。结构体的 / 结构体数组也是如此,只要它们最终都归结为标量类型。
即使在 ObjC 中,创建一个数组
SCNVector3
也可能有问题,因为该结构的元素类型在 32/64 位体系结构之间发生变化。sizeOfValue
Beta 2 中的数组似乎没有按预期工作,所以我建议提交一个 bug。如果
ArrayLiteralConvertible
在自定义结构中实现协议,则可以简洁地声明嵌套结构值的数组。
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):
如果您意识到这些问题,您可以在 Swift 中使用与 (Obj)C 中相同的顶点/索引缓冲区管理技巧,但有一些变化。例如,这里有一个片段,它使用交错的顶点和法线数据构建自定义几何体(这对 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])
回答by Toyos
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).
我对 swift 了解不多,但我希望你的“verts”和“indexes”版本在 swift 和 ObjC(实际上是 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.
在你的 C 版本中,你会得到一个 C 结构的 C 数组。在 swift 中,我假设你得到了一个包含在 NSValues 中的 SCNVector3 的快速“数组”(相当于 NSArray)。因此,您使用“索引”字节构建的 NSData 不包含像 C 中那样的浮点值的平面缓冲区。“verts”数组也是如此。
Looks like such C and swift interoperability issues are discussed here: Swift use c struct
看起来这里讨论了这样的 C 和 swift 互操作性问题:Swift use c struct