3

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 GLfloats, 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.

Community
  • 1
  • 1
Aaron Rasmussen
  • 13,082
  • 3
  • 42
  • 43

2 Answers2

4

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
Community
  • 1
  • 1
Bartek Banachewicz
  • 38,596
  • 7
  • 91
  • 135
  • The problem is that the parameter is of type `UnsafePointer`, so you can't just pass in an `Int` value. You have to pass a pointer. I've tested my struct in a playground and it is laid out the way that you suggest - I think you are right that the offset for the color data should be 16 bytes (`4 * sizeof(GLfloat)`) from the offset of the position data and I've tried a couple permutations of that, but it still crashes. – Aaron Rasmussen Dec 19 '15 at 21:30
  • @RomanSausarnes are you 100% positively sure the pointer had an actual value of 16?\ – Bartek Banachewicz Dec 19 '15 at 21:31
  • No, the pointer doesn't have an actual value of 16 bytes, but I took a pointer to the beginning of the struct, and advanced it by 16 bytes. – Aaron Rasmussen Dec 19 '15 at 21:33
  • 1
    @RomanSausarnes: Don't do that, you are not using client memory to store the vertex data. If you pass a pointer to CPU-side memory the GPU won't know what to do with it. Take a NULL pointer and add the offset to that. – Andon M. Coleman Dec 19 '15 at 21:56
  • @Andon: Thanks - how do you take a NULL pointer and add an offset to it? That sounds like the missing piece to my puzzle... – Aaron Rasmussen Dec 19 '15 at 21:58
  • 1
    @RomanSausarnes: In C, this would be something to the effect: `(float *)0 + `. I am not familiar with Objective C. GL just wants an integer offset, the function takes a pointer type but don't let that mislead you. When the data is GPU-side, it's an offset and should not be relative to any data stored CPU-side. – Andon M. Coleman Dec 19 '15 at 21:59
  • 2
    @RomanSausarnes What about `nil + 16`? Or [like this](http://stackoverflow.com/a/24899286/752976) – Bartek Banachewicz Dec 19 '15 at 22:04
  • @Bartek: That solved it. Works like a charm. Thank you to you and Andon. – Aaron Rasmussen Dec 19 '15 at 22:10
2

I defined Vertex structure like below:

struct Vertex {
var x : GLfloat = 0.0
var y : GLfloat = 0.0
var z : GLfloat = 0.0

var r : GLfloat = 0.0
var g : GLfloat = 0.0
var b : GLfloat = 0.0
var a : GLfloat = 1.0


init(_ x : GLfloat, _ y : GLfloat, _ z : GLfloat, _ r : GLfloat = 0.0, _ g : GLfloat = 0.0, _ b : GLfloat = 0.0, _ a : GLfloat = 1.0) {
    self.x = x
    self.y = y
    self.z = z

    self.r = r
    self.g = g
    self.b = b
    self.a = a
}

and then I set the vertices

let vertices : [Vertex] = [
    Vertex( 1.0, -1.0, 0, 1.0, 0.0, 0.0, 1.0),
    Vertex( 1.0,  1.0, 0, 0.0, 1.0, 0.0, 1.0),
    Vertex(-1.0,  1.0, 0, 0.0, 0.0, 1.0, 1.0),
    Vertex(-1.0, -1.0, 0, 1.0, 1.0, 0.0, 1.0)
]

let indices : [GLubyte] = [
    0, 1, 2,
    2, 3, 0
]

I use GLKViewController, so glkView(view:drawInRect:) function like below:

override func glkView(view: GLKView, drawInRect rect: CGRect) {
    glClearColor(1.0, 0.0, 0.0, 1.0);
    glClear(GLbitfield(GL_COLOR_BUFFER_BIT))

    shader.prepareToDraw()

    glEnableVertexAttribArray(VertexAttributes.Position.rawValue)
    glVertexAttribPointer(
        VertexAttributes.Position.rawValue,
        3,
        GLenum(GL_FLOAT),
        GLboolean(GL_FALSE),
        GLsizei(sizeof(Vertex)), BUFFER_OFFSET(0))


    glEnableVertexAttribArray(VertexAttributes.Color.rawValue)
    glVertexAttribPointer(
        VertexAttributes.Color.rawValue,
        4,
        GLenum(GL_FLOAT),
        GLboolean(GL_FALSE),
        GLsizei(sizeof(Vertex)), BUFFER_OFFSET(3 * sizeof(GLfloat))) // x, y, z | r, g, b, a :: offset is 3*sizeof(GLfloat)

    glBindBuffer(GLenum(GL_ARRAY_BUFFER), vertexBuffer)
    glBindBuffer(GLenum(GL_ELEMENT_ARRAY_BUFFER), indexBuffer)
    glDrawElements(GLenum(GL_TRIANGLES), GLsizei(indices.count), GLenum(GL_UNSIGNED_BYTE), nil)

    glDisableVertexAttribArray(VertexAttributes.Position.rawValue)

}

the BUFFER_OFFSET function is defined like below:

func BUFFER_OFFSET(n: Int) -> UnsafePointer<Void> {
    let ptr: UnsafePointer<Void> = nil
    return ptr + n
}

I can get the result

enter image description here

Great Thanks @Bartek!

BurtK
  • 1,016
  • 13
  • 12
  • 1
    This works for now, but an important hing to be aware of is that the layout of Swift structs is not officially defined or guaranteed to be stable across compiler versions. You may find you have to adjust this when/if the Swift compiler changes. If you need to be absolutely certain of data layout/packing, define a struct type in C and use it from Swift. – rickster Feb 26 '16 at 16:04
  • @rickster First of all, Great thanks for your advice. I am just learning about GLKit and Swift for now. :) – BurtK Feb 27 '16 at 12:16