Может ли механизм UnsafePointer раскрыть массив векторов в буфере данных для использования в SceneKit?

У меня есть двоичный файл, содержащий множество троек значений Float; на самом деле это длинный массив SCNVector3, представляющий вершины для SCNGeometrySource. Я прочитал файл (извините за упущение do-try-catch и т. д.) в память:

let sceneVectors = Data(contentsOf: sceneURL)

и хотите передать это в данных памяти как [SCNVector3] вместе с SceneKit:

let sceneGeometry = SCNGeometrySource(vertices: sceneVectors)

Сегодня я боролся с использованием механизма UnsafePointer для повторного ввода содержимого буфера Data в виде массива SCNVector3 для выполнения контракта SCNGeometrySource; что-то вроде этого.

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

Мое прочтение об этом говорит мне, что в замыкании к буферу можно получить доступ как к массиву SCNVector3, но при передаче в SCNGeometrySource Swift сообщает:

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

Как обычно, я подозреваю, что есть простое решение, которое я еще не придумал, поэтому я был бы признателен за любое предложение по решению этой проблемы.


person Ramsay Consulting    schedule 14.06.2017    source источник


Ответы (1)


В SCNGeometrySource(vertices: sceneVectors) вам нужно передать массив векторов, а не указатель:

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

В Swift 3 (но уже не в Swift 4) SCNGeometrySource имеет другой инициализатор, который принимает указатель на SCNVector3 и счетчик:

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

который можно замкнуть на

let sceneGeometry = dataBuffer.withUnsafeBytes {
    SCNGeometrySource(vertices: $0, count: dataBuffer.count/MemoryLayout<SCNVector3>.stride)
}
person Martin R    schedule 14.06.2017
comment
Шикарно описано, спасибо. Я отмечу, что инициализатор, который принимает указатель на SCNVector3 и количество, недоступен в Swift 4. Ошибка заключается в ошибке: 'init(vertices:count:)' недоступен в Swift: вместо этого используйте init(vertices:) . . - person Ramsay Consulting; 14.06.2017
comment
Я должен отметить для будущих читателей, что самое первое предложение исходного поста содержит ошибку. Я подразумевал, что SCNVector3 является тройкой Float, хотя на самом деле это тройка Double. Мой файл на самом деле содержит Float значений (использование 64-битных значений кажется немного чрезмерным для вектора SceneKit), поэтому мне еще предстоит кое-что сделать, однако Мартин правильно ответил на вопрос, который я задал. - person Ramsay Consulting; 14.06.2017
comment
@RamsayConsulting: согласно developer.apple.com/documentation/scenekit/scnvector3 это CGFloat на Mac и Float на iOS. - person Martin R; 14.06.2017
comment
В любом случае это 64-битные значения на 64-битных процессорах. Мой файл будет работать с вашим кодом на 32-битном процессоре. .com/documentation/coregraphics/cgfloat - person Ramsay Consulting; 14.06.2017
comment
Конечно, если я хочу избежать буферного копирования, я могу [должен?] вернуться к довольно ужасному использованию SCNGeometrySource(data: semantic: vectorCount: usesFloatComponents: componentsPerVector: bytesPerComponent: dataOffset: dataStride: ). - person Ramsay Consulting; 14.06.2017
comment
@RamsayConsulting: Это звучит разумно — я признаю, что ответил на вопрос, но почти ничего не знаю о SceneKit и SCNGeometrySource :) - person Martin R; 14.06.2017