Пользовательская геометрия SceneKit в Swift на iOS не работает, но эквивалентный код Objective C делает
Я стремлюсь принять новый язык Swift, так как кажется, что это путь к развитию Apple. Я также был впечатлен новой поддержкой SceneKit в iOS 8. Я хотел бы программно создавать пользовательскую геометрию во время выполнения, но я изо всех сил пытаюсь заставить работать код Swift. И все же эквивалентный код в Objective C работает нормально.
Это может быть ошибка или что-то не так.
Я просто пытаюсь создать и визуализировать один треугольник. Я буду игнорировать нормали, текстуры и т. Д. Для простоты. Так что я ожидаю увидеть только черный треугольник.
Swift Code (не работает)
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 (который работает)
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];
Любые указатели будут оценены.
3 ответа
Ну, два куска кода не переводятся точно друг к другу. int
в С не совпадает с Int
в Свифте. На самом деле это называется CInt
в Свифте:
/// The C 'int' type.
typealias CInt = Int32
Если вы измените оба вхождения для использования CInt
вместо этого сообщение об ошибке, которое вы получили ранее, исчезает (по крайней мере, для меня в 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, а не ObjC
NSArray
изNSValue
обернутые структуры. И вы можете передать массив Swift в (Obj)C API, которые принимаютCMutablePointer
или похожий, так что это совершенно крутоNSData
из массива Swift.То же самое относится и к массивам / структурам структур, при условии, что в конце они все переходят к скалярным типам.
Даже в ObjC, делая массив
SCNVector3
может быть проблематичным, потому что тип элемента этой структуры изменяется между 32/64-битными архитектурами.sizeOfValue
похоже, не работает должным образом для массивов в бета-версии 2, поэтому я бы порекомендовал оставить сообщение об ошибке.Если вы реализуете
ArrayLiteralConvertible
Протокол в ваших пользовательских структурах, вы можете кратко объявить массивы вложенных структурных значений.
Если вам известны эти проблемы, вы можете использовать в Swift, с некоторыми вариациями, большинство тех же приемов для управления буфером вершин / индексов, что и в (Obj)C. Например, вот фрагмент, который строит пользовательскую геометрию с чередованными вершинами и обычными данными (что хорошо для производительности на графических процессорах устройства iOS):
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 структур. В быстром я предполагаю, что вы получаете быстрый "массив" (эквивалент NSArray) SCNVector3, обернутый в NSValues. поэтому NSData, который вы строите с байтами "indexes", не содержит плоский буфер значений с плавающей запятой, как в C. То же самое для массива "verts".
Похоже, что такие вопросы C и swift обсуждаются здесь: Swift использует c struct