Custom SceneKit Geometry in Swift on iOS not working but equivalent Objective C code does

后端 未结 3 1772
南笙
南笙 2020-12-06 02:17

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

相关标签:
3条回答
  • 2020-12-06 02:28

    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:

    enter image description here

    0 讨论(0)
  • 2020-12-06 02:42

    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

    0 讨论(0)
  • 2020-12-06 02:52

    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])
    
    0 讨论(0)
提交回复
热议问题