Bisakah mekanisme `UnsafePointer` menampilkan array vektor dalam buffer Data untuk digunakan di SceneKit?

Saya memiliki file biner yang berisi banyak nilai tiga kali lipat Float; ini sebenarnya adalah array panjang SCNVector3 yang mewakili simpul untuk SCNGeometrySource. Saya membaca file (maafkan kelalaian do-try-catch, dll) ke dalam memori:

let sceneVectors = Data(contentsOf: sceneURL)

dan ingin meneruskan data memori itu sebagai [SCNVector3] ke SceneKit:

let sceneGeometry = SCNGeometrySource(vertices: sceneVectors)

Saya telah bergulat hari ini dengan menggunakan mekanisme UnsafePointer untuk mengetik ulang konten buffer Data sebagai array SCNVector3 untuk memenuhi kontrak SCNGeometrySource; sesuatu seperti ini.

dataBuffer.withUnsafeBytes { 
    (vertexBuffer: UnsafePointer<SCNVector3>) -> SCNGeometry in
        « magic happens here »
        return sceneGeometry
}

Pembacaan saya tentang ini memberi tahu saya bahwa, dalam penutupan, buffer dapat diakses sebagai array SCNVector3, tetapi ketika diteruskan ke SCNGeometrySource hal ini menyebabkan Swift melaporkan:

cannot convert value of type 'UnsafePointer<SCNVector3>' to expected argument type '[SCNVector3]'

Seperti biasa, saya curiga ada solusi sederhana yang belum saya temukan, jadi saya sangat menghargai saran apa pun untuk menyelesaikannya.


person Ramsay Consulting    schedule 14.06.2017    source sumber


Jawaban (1)


Di SCNGeometrySource(vertices: sceneVectors) Anda harus meneruskan array vektor, bukan pointer:

let sceneGeometry = dataBuffer.withUnsafeBytes {
    (vertexBuffer: UnsafePointer<SCNVector3>) -> SCNGeometrySource in
    let sceneVectors = Array(UnsafeBufferPointer(start: vertexBuffer, count: dataBuffer.count/MemoryLayout<SCNVector3>.stride))
    return SCNGeometrySource(vertices: sceneVectors)
}

Di Swift 3 (tetapi tidak lagi di Swift 4) SCNGeometrySource memiliki penginisialisasi lain yang mengambil pointer ke SCNVector3 dan menghitung:

let sceneGeometry = dataBuffer.withUnsafeBytes {
    (vertexBuffer: UnsafePointer<SCNVector3>) -> SCNGeometrySource in
    return SCNGeometrySource(vertices: vertexBuffer, count: dataBuffer.count/MemoryLayout<SCNVector3>.stride)
}

yang dapat disingkat menjadi

let sceneGeometry = dataBuffer.withUnsafeBytes {
    SCNGeometrySource(vertices: $0, count: dataBuffer.count/MemoryLayout<SCNVector3>.stride)
}
person Martin R    schedule 14.06.2017
comment
Dijelaskan dengan elegan, terima kasih. Saya perhatikan bahwa penginisialisasi yang mengambil pointer ke SCNVector3 dan hitungan tidak tersedia di Swift 4. Kesalahannya adalah error: 'init(vertices:count:)' is unavailable di Swift: Gunakan init(vertices:) sebagai gantinya. . - person Ramsay Consulting; 14.06.2017
comment
Saya harus mencatat, untuk pembaca selanjutnya, bahwa kalimat pertama dari postingan asli mengandung kesalahan. Maksud saya SCNVector3 adalah tripel dari Float padahal sebenarnya merupakan tripel dari Double. File saya sebenarnya berisi nilai Float (menggunakan nilai 64-bit sepertinya agak berlebihan untuk vektor SceneKit) jadi saya masih memiliki beberapa pekerjaan yang harus dilakukan, namun Martin menjawab pertanyaan saya dengan benar. - person Ramsay Consulting; 14.06.2017
comment
@RamsayConsulting: Menurut developer.apple.com/documentation/scenekit/scnvector3 memang demikian CGFloat di Mac dan Float di iOS. - person Martin R; 14.06.2017
comment
Apa pun itu, nilainya adalah 64-bit pada CPU 64-bit. File saya akan sesuai dengan kode Anda, pada CPU 32-bit .. developer.apple .com/documentation/coregraphics/cgfloat - person Ramsay Consulting; 14.06.2017
comment
Tentu saja, jika saya ingin menghindari salinan buffer, saya dapat [harus?] kembali ke penggunaan SCNGeometrySource(data: semantic: vectorCount: usesFloatComponents: componentsPerVector: bytesPerComponent: dataOffset: dataStride: ) yang agak mengerikan. - person Ramsay Consulting; 14.06.2017
comment
@RamsayConsulting: Kedengarannya masuk akal – Saya akui bahwa saya menjawab pertanyaan tetapi hampir tidak tahu apa-apa tentang SceneKit dan SCNGeometrySource :) - person Martin R; 14.06.2017