How to use GLKMatrixStack in Objective-C? (GLKit)

730 views Asked by At

I'm trying to port over some OpenGL code from an Android project to iOS. I've done a little bit of iOS programming before so i'm familiar with the syntax of Objective-C but I can't seem to figure out how these GLKMatrixStacks work. Here is some Java code that I am trying to port with an example of how I need to use the stack.

public void drawLeftHip()
{
    float[] temp = new float[16];
    System.arraycopy(mModelMatrix, 0, temp, 0, mModelMatrix.length);
    stack.push(temp);

    //Global transformations here
    Matrix.translateM(mModelMatrix, 0, -1.0f, -.75f, 0.0f);
    Matrix.rotateM(mModelMatrix, 0, lThighAngle, 1.0f, 0.0f, 0.0f);  

    //draw children
    drawLeftThigh();

    //left hip
    Matrix.scaleM(mModelMatrix, 0, .25f, .25f, .25f);
    drawPackedTriangleBuffer(mSphereData, mSphereVertexCount, mModelMatrix, mColorBlack); //draw the triangle with the given model matrix

    mModelMatrix = stack.pop();
}

I basically make a copy of mModelMatrix (a 4x4 matrix) and push it on the stack, do my global transformations, draw children, do local transformations, and finally pop the stack.

I've tried using a NSMutableArray for the purpose of a stack but I can't use it because I need to use GLKMatrix4 which is not if type (id).

1

There are 1 answers

0
rickster On BEST ANSWER

GLKMatrix4 represents a 4x4 matrix. It's just a union encapsulating a 16-element float array, so it's analogous to the float[16] array in your example. If you work with GLKBaseEffect you're already using these for your ModelView and Projection matrices. (If you're rolling your own shaders, using the GLKit matrix/vector types and corresponding math utilities is a good idea anyway, since they're optimized to provide good performance on iOS devices.)

To follow the pattern of your example:

  1. Use GLKMatrixStackCreate to create a stack, then GLKMatrixStackLoad to copy your current ModelView matrix onto the stack. This function automatically copies the matrix so you don't need to create a copy yourself as in your example.

  2. Use GLKMatrix4Translate, GLKMatrix4Rotate, etc to transform your ModelView matrix before drawing with it. (Don't forget to upload the changed matrices to your shader's uniform variables, or if using GLKBaseEffect update its transform property and tell it to prepareToDraw, before drawing.)

  3. Finally, use one of the GLKMatrixStackGet functions to retrieve the saved matrix from the stack.

This pattern doesn't actually make use of the stack, though -- it just saves and loads a single matrix. You can extend the pattern by using GLMatrixStackPush to save additional matrices and GLKMatrixStackPop to restore to a prior state. (The pop function doesn't return the top matrix before disposing of it, so use a GLKMatrixStackGet function if you want to retrieve it first.) This pattern still doesn't make optimal use of the stack, though -- a better way is to keep all your matrix operations in the stack. Here's a quick (untested) example:

// subclass of GLKViewController as in the "OpenGL Game" Xcode template
@implementation MyViewController
{
    GLKMatrixStack mvStack;
}

- (void)setupGL
{
    // ... other setup code ...

    // set up base transform
    mvStack = GLKMatrixStackCreate(NULL);
    GLKMatrixStackTranslate(mvStack, /* ... coords ... */);
    GLKMatrixStackScale(mvStack, /* ... coords ... */);
    GLKMatrixStackRotate(mvStack, /* ... coords ... */);
}

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
    // save the base transform
    GLKMatrixStackPush(mvStack);

    // additional transforms for figure
    GLKMatrixStackTranslate(mvStack, /* ... coords ... */);
    GLKMatrixStackScale(mvStack, /* ... coords ... */);
    GLKMatrixStackRotate(mvStack, /* ... coords ... */);

    [self drawBody];

    // restore the base transform
    GLKMatrixStackPop(mvStack);
}

- (void)drawBody
{
    self.effect.transform.modelviewMatrix = GLKMatrixStackGetMatrix4(mvStack);
    // ... draw body ...

    // save body transform 
    GLKMatrixStackPush(mvStack);

    // transform to left leg coordinate space
    GLKMatrixStackTranslate(mvStack, /* ... coords ... */);
    GLKMatrixStackScale(mvStack, /* ... coords ... */);
    GLKMatrixStackRotate(mvStack, /* ... coords ... */);

    [self drawLeg];

    // restore to body coordinates
    GLKMatrixStackPop(mvStack);

    // transform to right leg coordinate space
    GLKMatrixStackTranslate(mvStack, /* ... coords ... */);
    GLKMatrixStackScale(mvStack, /* ... coords ... */);
    GLKMatrixStackRotate(mvStack, /* ... coords ... */);

    [self drawLeg];

    // restore to body coordinates
    GLKMatrixStackPop(mvStack);
}

- (void)drawLeg
{
    self.effect.transform.modelviewMatrix = GLKMatrixStackGetMatrix4(mvStack);
    // ... draw upper leg ...

    // save upper leg transform 
    GLKMatrixStackPush(mvStack);

    // transform to lower leg coordinate space
    GLKMatrixStackTranslate(mvStack, /* ... coords ... */);
    GLKMatrixStackScale(mvStack, /* ... coords ... */);
    GLKMatrixStackRotate(mvStack, /* ... coords ... */);

    self.effect.transform.modelviewMatrix = GLKMatrixStackGetMatrix4(mvStack);
    // ... draw lower leg ...

    // restore to upper leg coordinates
    GLKMatrixStackPop(mvStack);
}

Read the documentation on GLKMatrixStack for more info.