Understanding the Buffer better XCODE / OBJ-C / OPENGL ES 2.0

So, I took the wonderful Xcode-provided template for: OpenGL Game.

The basis is that it loads a cube into a vertex array, and then copies itself twice on the screen does some really basic animation.

So I switched that cube out for a simple 2d plane, and thus far have been happy with how my learning has been progressing. My next step naturally, was to put back in the cube… on top of my 2dplane( my terrain ), and then Ill have the camera follow this ‘cube’ around.

So heres the problem though:
I created another GLFLoat [216] with all the vertices and normals, and I have the camera setup so I CAN view both the 2dplane and the 3dcube without any changes…

But, how do I add this (2nd object= the cube) into the data stream so that it behaves like a completely seperate object… NOT binded to the original 2d plane.

Those who are using XCODE and OpenGL know that know we have the wonderful GLKIT to perform alot of these difficult operations below. And Im afraid that if I simple ‘stack’ this 2nd object(3dcube) to the end of the array right before it goes into the buffer. Then I assume that the 2dplane and the 3dobjects are all thought of as the same “object”… and so If I do any transformations on the data, it will probably affect ALL the data. The code is below:


GLfloat gCubeVertexData[216] =
{
    // Data layout for each line below is:
    // positionX, positionY, positionZ,     normalX, normalY, normalZ,
    0.5f, -0.5f, -0.5f,        1.0f, 0.0f, 0.0f,
    0.5f, 0.5f, -0.5f,         1.0f, 0.0f, 0.0f,
    0.5f, -0.5f, 0.5f,         1.0f, 0.0f, 0.0f,
    0.5f, -0.5f, 0.5f,         1.0f, 0.0f, 0.0f,
    0.5f, 0.5f, -0.5f,          1.0f, 0.0f, 0.0f,
    0.5f, 0.5f, 0.5f,         1.0f, 0.0f, 0.0f,
    
    0.5f, 0.5f, -0.5f,         0.0f, 1.0f, 0.0f,
    -0.5f, 0.5f, -0.5f,        0.0f, 1.0f, 0.0f,
    0.5f, 0.5f, 0.5f,          0.0f, 1.0f, 0.0f,
    0.5f, 0.5f, 0.5f,          0.0f, 1.0f, 0.0f,
    -0.5f, 0.5f, -0.5f,        0.0f, 1.0f, 0.0f,
    -0.5f, 0.5f, 0.5f,         0.0f, 1.0f, 0.0f,
    
    -0.5f, 0.5f, -0.5f,        -1.0f, 0.0f, 0.0f,
    -0.5f, -0.5f, -0.5f,       -1.0f, 0.0f, 0.0f,
    -0.5f, 0.5f, 0.5f,         -1.0f, 0.0f, 0.0f,
    -0.5f, 0.5f, 0.5f,         -1.0f, 0.0f, 0.0f,
    -0.5f, -0.5f, -0.5f,       -1.0f, 0.0f, 0.0f,
    -0.5f, -0.5f, 0.5f,        -1.0f, 0.0f, 0.0f,
    
    -0.5f, -0.5f, -0.5f,       0.0f, -1.0f, 0.0f,
    0.5f, -0.5f, -0.5f,        0.0f, -1.0f, 0.0f,
    -0.5f, -0.5f, 0.5f,        0.0f, -1.0f, 0.0f,
    -0.5f, -0.5f, 0.5f,        0.0f, -1.0f, 0.0f,
    0.5f, -0.5f, -0.5f,        0.0f, -1.0f, 0.0f,
    0.5f, -0.5f, 0.5f,         0.0f, -1.0f, 0.0f,
    
    0.5f, 0.5f, 0.5f,          0.0f, 0.0f, 1.0f,
    -0.5f, 0.5f, 0.5f,         0.0f, 0.0f, 1.0f,
    0.5f, -0.5f, 0.5f,         0.0f, 0.0f, 1.0f,
    0.5f, -0.5f, 0.5f,         0.0f, 0.0f, 1.0f,
    -0.5f, 0.5f, 0.5f,         0.0f, 0.0f, 1.0f,
    -0.5f, -0.5f, 0.5f,        0.0f, 0.0f, 1.0f,
    
    0.5f, -0.5f, -0.5f,        0.0f, 0.0f, -1.0f,
    -0.5f, -0.5f, -0.5f,       0.0f, 0.0f, -1.0f,
    0.5f, 0.5f, -0.5f,         0.0f, 0.0f, -1.0f,
    0.5f, 0.5f, -0.5f,         0.0f, 0.0f, -1.0f,
    -0.5f, -0.5f, -0.5f,       0.0f, 0.0f, -1.0f,
    -0.5f, 0.5f, -0.5f,        0.0f, 0.0f, -1.0f
};

GLfloat g2dPlaneData[36] =
{
    // Data layout for each line below is:
    // positionX, positionY, positionZ,     normalX, normalY, normalZ,
    -0.5f, -0.0f, -0.5f,       0.0f, 0.1f, 0.0f,
    0.5f, -0.0f, -0.5f,        0.0f, 0.1f, 0.0f,
    -0.5f, -0.0f, 0.5f,        0.0f, 0.1f, 0.0f,
    -0.5f, -0.0f, 0.5f,        0.0f, 0.1f, 0.0f,
    0.5f, -0.0f, -0.5f,        0.0f, 0.1f, 0.0f,
    0.5f, -0.0f, 0.5f,         0.0f, 0.1f, 0.0f,
    
};

and then the SETUP


- (void)setupGL
{
    [EAGLContext setCurrentContext:self.context];
    //int totalNumberOfObjects;
    [self loadShaders];
    
    self.effect = [[GLKBaseEffect alloc] init];
    self.effect.light0.enabled = GL_TRUE;
    self.effect.light0.diffuseColor = GLKVector4Make(1.0f, 0.4f, 0.4f, 1.0f);
    
    glEnable(GL_DEPTH_TEST);
    
    //for (totalNumberOfObjects = 1; totalNumberOfObjects == 2; totalNumberOfObjects ++);{
    //}
    // Damnit, I dont think this is the way.....
    
    glGenVertexArraysOES(1, &_vertexArray);
    glBindVertexArrayOES(_vertexArray);
    
    glGenBuffers(1, &_vertexBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
    
    glBufferData(GL_ARRAY_BUFFER, sizeof(g2dPlaneData), g2dPlaneData, GL_STATIC_DRAW);
   // glBufferData(GL_ARRAY_BUFFER, sizeof(gCubeVertexData), gCubeVertexData, GL_STATIC_DRAW);
    // I can SWITCH out EITHER of the above lines of code and it will still work...
    // But, how do I add both of them, and also have each object be independent in the UPDATE methods
    
    glEnableVertexAttribArray(GLKVertexAttribPosition);
    glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(0));
    glEnableVertexAttribArray(GLKVertexAttribNormal);
    glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(12));
    
    glBindVertexArrayOES(0);
}

And then the UPDATE methods:


- (void)update
{
    float aspect = fabsf(self.view.bounds.size.width / self.view.bounds.size.height);
    GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(65.0f), aspect, 0.1f, 100.0f);
    
    self.effect.transform.projectionMatrix = projectionMatrix;
    
    GLKMatrix4 baseModelViewMatrix = GLKMatrix4MakeTranslation(0.0f, -0.0, -2.0f);
    //NSLog (@"%f", _rotation);
    //both objects will rotate as a group
     baseModelViewMatrix = GLKMatrix4Scale (baseModelViewMatrix, 1.0f, 1.0f, 1.0f);
   // baseModelViewMatrix = GLKMatrix4Rotate(baseModelViewMatrix, (_rotation/3), 1.0f, 0.0f, 0.0f);
    
    // Compute the model view matrix for the object rendered with GLKit
    GLKMatrix4 modelViewMatrix = GLKMatrix4MakeTranslation(0.0f, 0.0f, 0.0f);
    //both objects rotated on its axis as individuals below
    //modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix, _rotation, 0.0f, 1.0f, 0.0f);
    modelViewMatrix = GLKMatrix4Multiply(baseModelViewMatrix, modelViewMatrix);
    
    
    // Look @ Variables<#float eyeX#>,<#float eyeY#>,<#float eyeZ#>,<#float centerX#>,<#float centerY#>,<#float centerZ#>,<#float upX#>,<#float upY#>,<#float upZ#>
    // eye x,y,z = eye coordinates
    // center x,y,z = target coordinates
    // up x,y,z = cameras up vector
    //
    //
    //
    GLKMatrix4 viewMatrix = GLKMatrix4MakeLookAt(0.0f, 2.0f, 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f);
    
    self.effect.transform.modelviewMatrix = GLKMatrix4Multiply (viewMatrix, modelViewMatrix);
    
    // Compute the model view matrix for the object rendered with ES2
    //modelViewMatrix = GLKMatrix4MakeTranslation(0.0f, 0.0f, 1.5f);
    //modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix, _rotation, 1.0f, 1.0f, 1.0f);
    //modelViewMatrix = GLKMatrix4Multiply(baseModelViewMatrix, modelViewMatrix);
    
    _normalMatrix = GLKMatrix3InvertAndTranspose(GLKMatrix4GetMatrix3(modelViewMatrix), NULL);
    
    _modelViewProjectionMatrix = GLKMatrix4Multiply(projectionMatrix, modelViewMatrix);
    
    _rotation += self.timeSinceLastUpdate * 0.5f;
}

As you can see from my comments in the SETUP code, I can attach either object to the buffer… But Im not understanding the process for adding both.

So understandably, I have a few questions:

  1. You can’t just add the cube data onto the stack for the buffer right? Otherwise, wouldnt the Update methods apply to the entire data sets within? Example: Meaning, you couldnt manipulate specific objects in their own-right way… like rotating a cube, but maybe you have another cube in the stack that needs to move in a completely separate way.

  2. If you do still have to add onto the stack of the vertex buffer… You would probably do like an NSARRAY: add with another?

3.Im getting confused with the baseModelView and how it goes up the heiarchy to the view… I understand how it needs to transform from 3d coordinates and multiplied into the eye’s view… etc and into 2d coordinates for the screen. I think I understand that the baseModelView is created based on what was previously assigned to the buffer, as my objects?? meaning both objects, the plane and the cube, are really understood in my program to be the same object? Is this a correct assumption?
How do I manipulate each object independently in my UPDATE method?

There are two levels of buffering here. To keep the objects separate, you may want separate vertex buffers (VBOs) for each, but this is not essential. The vertex array object (VAO) encapsulates more state, including the creation of the vertex attribute pointers and the enabling of the vertex attribute arrays. You can also use separate VAOs for each object, but this again is not essential. The key is that both these constructs encapsulate model state, but neither tells GLES what to draw, and where, by itself. The essence of both is to set up state on the GPU with as few transactions as possible.

Look at the DrawInRect method in the template. The glDrawArrays call is where things actually happen. The glBindVertexArrayOES call restores the state, including the buffer bindings, pointer mappings and enabling of vertex attribute arrays, that was in place when the VAO was unbound. The glDrawArrays call then accesses those arrays to bet the vertex attribute to use to draw the primitives (GL_TIANGLES, in this case). Since the cube consists of 12 triangles, 36 vertices are used. The glDrawArray arguments specify use of vertices 0 - 36 in the bound arrays. If you had concatenated your VBOs and used a single VAO, you could still draw two objects with independent calls by providing a different range of vertices in each call.

You can also use element arrays (not used in the template) to specify sets of indices into the vertices, rather than a straight sequence, using the GL_ELEMENT_ARRAY buffer type (for now, save htis for another day).

Now to your question The update method, as supplied in the template, does nothing that changes the vertices or other state bound by the VBO or VAO! Rather, it is solely concerned with manipulating the transform matrices held by the effect (GLKBaseEffect) member of your view controller. These matrices have no effect on OpenGL’s state until the prepareToDraw method is called for the effect member, at which time they are combined into a model/view/projection (MVP) matrix, and sent, as a ‘uniform’ variable, to the GPU for use by the vertex shader supplied by GLKit. The shader uses this combined matrix to transform the vertex coordinates in subsequent draw calls ito the “eye space” coordinates used for display.

What all this means is that, if you want to rotate or translate one object independent of the other, you need to change the transform settings between the calls to draw each object. For example, if you wanted GLKit to render a second cube at the position the template uses for direct rendering with ES2, you could do something like the following in your drawInRect method:


    // Render the object with GLKit
    [self.effect prepareToDraw];
    glDrawArrays(GL_TRIANGLES, 0, 36);

    // Change the transform
    GLKMatrix4 temp = self.effect.transform.modelViewMatrix;
    self.effect.transform.modelViewMatrix = _modelViewMatrix;  // you need to add this member to your class, and set it at the end of your update method

    // Draw again
    [self.effect prepareToDraw];
    glDrawArrays(GL_TRIANGLES, 0, 36);

    //Reset the transform
    self.effect.transform.modelViewMatrix = temp;

As for the particulars of how the transform matrices are manipulated in the update method, I will defer to someone better qualified to explain the thinking for setting up the rotations and translations.