I'm translating the Objective-C code in this repo into Swift, in order to learn some of the basics of OpenGL. I'm totally new to it. I've got a working project that compiles and produces a working NSOpenGLView with a floating rectangle, but the colors are wrong. I've narrowed the problem down to my use of the glVertexAttribPointer
functions that point to the vertex and color data.
Here is how I have my vertex and color data stored:
struct Vertex {
var position: (x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat)
var color: (r: GLfloat, g: GLfloat, b: GLfloat, a: GLfloat)
}
struct Vertices {
var v1: Vertex
var v2: Vertex
var v3: Vertex
var v4: Vertex
}
var vertexData = Vertices(
v1: Vertex( position: (x: -0.5, y: -0.5, z: 0.0, w: 1.0),
color: (r: 1.0, g: 0.0, b: 0.0, a: 1.0)),
v2: Vertex( position: (x: -0.5, y: 0.5, z: 0.0, w: 1.0),
color: (r: 0.0, g: 1.0, b: 0.0, a: 1.0)),
v3: Vertex( position: (x: 0.5, y: 0.5, z: 0.0, w: 1.0),
color: (r: 0.0, g: 0.0, b: 1.0, a: 1.0)),
v4: Vertex( position: (x: 0.5, y: -0.5, z: 0.0, w: 1.0),
color: (r: 1.0, g: 1.0, b: 1.0, a: 1.0)) )
The Objective-C versions of the glVertexAttribPointer
functions that I am trying to translate look like this:
glVertexAttribPointer((GLuint)positionAttribute, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid *)offsetof(Vertex, position));
glVertexAttribPointer((GLuint)colourAttribute , 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid *)offsetof(Vertex, colour ));
The Objective-C versions use the offsetof
macro to set the pointer parameter of these functions. Swift doesn't allow macros, and so I'm trying to figure out what to use in its place. I've tried passing nil
, like this:
glVertexAttribPointer(GLuint(positionAttribute), 4, UInt32(GL_FLOAT), UInt8(GL_FALSE), GLsizei(sizeof(Vertex)), nil)
glVertexAttribPointer(GLuint(colorAttribute), 4, UInt32(GL_FLOAT), UInt8(GL_FALSE), GLsizei(sizeof(Vertex)), nil)
But if I do that, the color data array is filled with the position data - no offset is taken into account, so it uses the position data for both the position and color attributes.
I found this stackoverflow answer which suggests using withUnsafePointer
and tried it out, like this:
withUnsafePointer(&vertexData.v1.position) { ptr in
glVertexAttribPointer(GLuint(positionAttribute), 4, UInt32(GL_FLOAT), UInt8(GL_FALSE), GLsizei(sizeof(Vertex)), ptr)
}
withUnsafePointer(&vertexData.v1.color) { ptr in
glVertexAttribPointer(GLuint(colorAttribute), 4, UInt32(GL_FLOAT), UInt8(GL_FALSE), GLsizei(sizeof(Vertex)), ptr)
}
But that crashes the whole display, requiring a forced shutdown and a reboot.
I'm not sure what to try next. The complete code that I'm working on is available here.
EDIT:
I've also tried taking a pointer to the first data point and advancing it by 4 GLfloat
s, like this:
let ptr = UnsafePointer<GLfloat>([vertexData.v1.position.x])
glVertexAttribPointer(GLuint(positionAttribute), 4, UInt32(GL_FLOAT), UInt8(GL_FALSE), GLsizei(sizeof(Vertex)), ptr)
glVertexAttribPointer(GLuint(colorAttribute), 4, UInt32(GL_FLOAT), UInt8(GL_FALSE), GLsizei(sizeof(Vertex)), ptr.advancedBy(4))
The display doesn't crash, but nothing is drawn to the screen at all.
To replicate the offsetof
functionality you just need to know at which byte offset does each field lie in your structure.
In your case, assuming your struct is tightly packed, the first call should receive 0
, and the second 4 * GLfloat
because that's the size of the first component. I'm not sure how to extract this data directly from the structure, especially since you're using tuples.
To illustrate, your structure:
struct Vertex {
var position: (x: GLfloat, y: GLfloat, z: GLfloat, w: GLfloat)
var color: (r: GLfloat, g: GLfloat, b: GLfloat, a: GLfloat)
}
is most probably laid out like that:
GLfloat // <-- structure start // <-- position
GLfloat
GLfloat
GLfloat
GLfloat // <-- color
GLfloat
GLfloat
GLfloat
Hence the position
lies at offset 0
, and color
at 4 * GLfloat
.
To create a pointer with a value 16, this answer looks relevant:
let ptr = UnsafePointer<()> + 16
So I suppose this should work as well:
let ptr = UnsafePointer<()> + sizeof(GLfloat) * 4