iDevGames Forums
Dissecting Apple's GLEssentials Demo - Printable Version

+- iDevGames Forums (http://www.idevgames.com/forums)
+-- Forum: Development Zone (/forum-3.html)
+--- Forum: Graphics & Audio Programming (/forum-9.html)
+--- Thread: Dissecting Apple's GLEssentials Demo (/thread-9360.html)



Dissecting Apple's GLEssentials Demo - dockode - Sep 25, 2011 02:57 PM

After some more "google-ing" I feel that I may be approaching learning OpenGL the wrong way. For one thing... many of the demos and tutorials I have found focus on "immediate mode" in OpenGL, something that I understand is not recommended.
More importantly, I did not have a rendering loop going on so user interaction was not possible. Fail

I found the GLEssentials demo on Apple's developer portal. Now there is a lot going on in this demo... they load a model, compile shaders, have several helper classes that handle things like vector and matrix math, and most importantly for me have a rendering loop. It is an interesting resource for a learner. But a bit complex too...

I tried all day today to strip this demo down to it's core. I feel sort of like a kid taking apart his Dad's drill only to discover he has no clue how to put it together again. I understand what the parts do, but not exactly how they go together again. I did however learn a bit about one way to structure an OpenGL application.

Today my goal was to rebuild the OpenGLRenderer class to draw an empty red OpenGL window, I'll worry about drawing objects later. I did this by trying to leave out the bit's that load the model and reflective quad. But I'm finding that doing this breaks the renderer. My efforts feel a bit Sisyphean.

Is there anyone who would be able to break down what is going on in the renderer class "OpenGLRenderer"? Smile If so I'd deeply appreciate it.

Here is the link to the source Apple provides:
Apple GLEssentials Demo

Thanks!

P.S. I'm waiting for the OpenGL SuperBible 5th edition to arrive.


RE: Dissecting Apple's GLEssentials Demo - dockode - Sep 25, 2011 09:20 PM

Interesting note about the NSGLView class for this project just in case anyone is curious... ZZZ

I found out that contrary to Apple's documentation found here (Step 11 just above figure 2-2):
OpenGL Programming Guide

When you edit the MainMenu.xib file in the GLEssentials demo, to add the NSGLView custom class to your Window you need to drag a "Custom View" object onto your NSView object instead of an NSOpenGLView object then change the class to NSGLView.

Now my NSGLView class is working the way it should (I think)... I presume this because I commented out the calls to the renderer in the GLEssentials demo and compared the results to my project. Both come up with a garbled screen, which previously my project was not. So I assume that this means I am on the right track (maybe that I now have an OpenGL context). Wacko

Sadly, I do not know why the "Custom View" object is needed in the GLEssentials demo and the recommendation of using an NSOpenGLView produces no result. When you create the project in the documentation in the above link it works fine. Is there anyone who can shed light on this? I'd love to know. Huh

Perhaps tomorrow after work I will have another crack at that "OpenGLRenderer" class. In the meantime I will spend a few hours contemplating my eyelids.

Thanks in advance for any responses.


RE: Dissecting Apple's GLEssentials Demo - OneSadCookie - Sep 25, 2011 11:54 PM

whether -initWithCoder:, -initWithFrame:pixelFormat: or -initWithFrame: is called depends on how you make your view in IB. This trips just about anyone up. Off the top of my head, I think subclasses of NSOpenGLView get -initWithCoder, subclasses of NSView get -initWithFrame: and NSOpenGLView itself gets -initWithFrame:pixelFormat:, but I could well be wrong!


RE: Dissecting Apple's GLEssentials Demo - ipeku - Sep 26, 2011 07:21 AM

- (id) initWithFrame:(NSRect)frameRect seems to be called in NSGLView.

dockode you wondered about the "custom view" I don't know if this answers your question but:

NSGLView is a subclass NSOpenGLView which is turn is a subclass of NSView. In IB you set the class that you want the NSView to be which in this case is NSGLView. If you select NSOpenGLView in IB it wouldn't be attached to NSGLView where your functions are.


RE: Dissecting Apple's GLEssentials Demo - dockode - Sep 26, 2011 06:31 PM

ipeku - True but doesn't dragging the NSOpenGLView into your NSView and then changing the class of the NSOpenGLView to NSGLView link it to your functions?

By the way thank you both for your answers. I'm beginning to understand the way IB works a little better. Smile


RE: Dissecting Apple's GLEssentials Demo - dockode - Sep 26, 2011 06:54 PM

So... it turns out that I was not breaking the "OpenGLRenderer" class after all. After I added my customized version of the class (which leaves out all the cool stuff the demo does) I was able to get my red window and I'm rendering with OpenGL 3.2! Yay. My problem had to do with how I was making connections in IB (NSGLView). Fail

So now it seems that I'm on the right track. I have a render loop, and am able to draw a red OpenGL window. Wow Now on to shapes again... I think the GLEssentials demo actually does this in the "modelUtil.c" file. The relevant code is below. Assuming I wanted to draw tiles and then use a "texture atlas", would this be a decent way to go about creating the geometry? Thanks!

Code:
demoModel* mdlLoadQuadModel()
{
    GLfloat posArray[] = {
        -200.0f, 0.0f, -200.0f,
         200.0f, 0.0f, -200.0f,
         200.0f, 0.0f,  200.0f,
        -200.0f, 0.0f,  200.0f
    };
        
    GLfloat texcoordArray[] = {
        0.0f,  1.0f,
        1.0f,  1.0f,
        1.0f,  0.0f,
        0.0f,  0.0f
    };
    
    GLfloat normalArray[] = {
        0.0f, 0.0f, 1.0,
        0.0f, 0.0f, 1.0f,
        0.0f, 0.0f, 1.0f,
        0.0f, 0.0f, 1.0f,
    };
    
    GLushort elementArray[] =
    {
        0, 2, 1,
        0, 3, 2
    };
    
    demoModel* model = (demoModel*) calloc(sizeof(demoModel), 1);
    
    if(NULL == model)
    {
        return NULL;
    }
    
    model->positionType = GL_FLOAT;
    model->positionSize = 3;
    model->positionArraySize = sizeof(posArray);
    model->positions = (GLubyte*)malloc(model->positionArraySize);
    memcpy(model->positions, posArray, model->positionArraySize);
    
    model->texcoordType = GL_FLOAT;
    model->texcoordSize = 2;
    model->texcoordArraySize = sizeof(texcoordArray);
    model->texcoords = (GLubyte*)malloc(model->texcoordArraySize);
    memcpy(model->texcoords, texcoordArray, model->texcoordArraySize );

    model->normalType = GL_FLOAT;
    model->normalSize = 3;
    model->normalArraySize = sizeof(normalArray);
    model->normals = (GLubyte*)malloc(model->normalArraySize);
    memcpy(model->normals, normalArray, model->normalArraySize);
    
    model->elementArraySize = sizeof(elementArray);
    model->elements    = (GLubyte*)malloc(model->elementArraySize);
    memcpy(model->elements, elementArray, model->elementArraySize);
    
    model->primType = GL_TRIANGLES;
    
    
    model->numElements = sizeof(elementArray) / sizeof(GLushort);
    model->elementType = GL_UNSIGNED_SHORT;
    model->numVertcies = model->positionArraySize / (model->positionSize * sizeof(GLfloat));
    
    return model;
}

Ponder


RE: Dissecting Apple's GLEssentials Demo - Skorche - Sep 26, 2011 07:21 PM

It's generally faster and easier to work with a packed vertex format. It's a lot more compact, easier to read and easier to write code to work with it IMO. Ex:
Code:
struct Vertex {
  struct {GLfloat x, y, z;} vertex;
  struct {GLfloat s, t;} texcoord;
  struct {GLfloat x, y, z;} normal;
};

struct Vertex vertexes[] = {
  {{-200, 0.0, -200}, {0, 1}, {0, 0, 1}},
  ..., //etc
}

That way when you need to move things around you only need to make a single memcpy() call and if your application uses the same vertex format everywhere, you can pass a single pointer instead of 2 or 3 or whatever.

When binding the arrays, just use the offsetof() macro to get the pointer locations and use sizeof(struct Vertex) to get your stride.


RE: Dissecting Apple's GLEssentials Demo - dockode - Sep 26, 2011 10:48 PM

- Skorche - I'm not going to lie, that was over my head. Blush [Edit] Thank you though. I am looking into this further. I think I'll try doing this in my animation class mentioned in the post below.

I am finding that what was easy to do in the fixed function pipeline is now very difficult to do in the programmable pipeline. There is just so much going on in this demo. Blink

I have basically stripped the functionality down to presenting an OpenGL window with a red background. I want to take the "render" method from the "OpenGLRenderer" class and just draw a simple quad. This is proving to be more difficult than something like:

Code:
glBegin(GL_TRIANGLES);
glVertex3f(100.0f, 100.0f, 0.0f);
glVertex3f(150.0f, 100.0f, 0.0f);
glVertex3f(125.0f, 50.0f, 0.0f);
glEnd( );

Now it seems that i need to create a Vertex Array Object, compile shaders, etc. Does anyone know of a simple demo of how this is done. Or is it really as complex (at least from a beginners point of view) as Apple's GLEssentials demo makes it out to be?

In the meantime I will continue to pick it apart of course. Rasp


RE: Dissecting Apple's GLEssentials Demo - dockode - Sep 27, 2011 09:40 PM

Quote: dockode- I can't believe I am having so much trouble with this. This is my version of the OpenGLRenderer class, which draws a gray background and is supposed to draw a triangle. Any thoughts on what I'm doing wrong as far as drawing is concerned... If you need the full project I can provide that too... Though I just modified the GLEssentials project to use this for rendering.

Edit: I removed the code to make the thread shorter and to avoid another double post.

I am now able to draw a few quads on screen. Wow So on to the next step... adding functionality to the GLEssentials demo. Rasp

I would like to animate a simple model. I understand that keyframe animation is the easiest way to go, and thus for me the best way to go for now. Regarding a model... I am referring to a simple hand coded model. No model loading yet. One thing at a time right?

I imagine that a class would be better than a struct for this purpose.

I am figuring that to accomplish animation I would have the following in the class:
1) An array of vertices representing the current frame.
2) An array of vertex arrays containing all possible poses / keyframes.
3) A function to update the current frame based on a timeframe.

Obviously this is probably extremely limited as it doesn't allow me to create multiple sets of animations... but again... one thing at a time right?
Maybe it's as simple as an struct within the class containing a start and end index as well as a name for each animation?

Assuming that this is more or less the way to go about creating the "model" class... what is the best way to update data in the render loop? Do I recreate the entire Vertex Array Object just to update vertex data? Or is there another way? I was playing around with it already just as a proof of concept and I did the following within my render loop:
Code:
// There is a variable being incremented each time the render loop runs called _stringAnimStep for now as I have no class to handle model animation.
    if (_stringAnimStep == 0) {
        _stringModelA = mdlLoadStringModelA();
        _stringVAOName = [self buildVAO:_stringModelA]; // Here I am rebuilding the entire Vertex Array Object to draw my model.
        
        _stringNumElements = _stringModelA->numElements;
        _stringPrimType = _stringModelA->primType;
        _stringElementType = _stringModelA->elementType;
        
        mdlDestroyModel(_stringModelA);
        _stringModelA = NULL;
    }
    if (_stringAnimStep == 6) {
        _stringModelA = mdlLoadStringModelB();
        _stringVAOName = [self buildVAO:_stringModelA];
        
        _stringNumElements = _stringModelA->numElements;
        _stringPrimType = _stringModelA->primType;
        _stringElementType = _stringModelA->elementType;
        
        mdlDestroyModel(_stringModelA);
        _stringModelA = NULL;
    }
    if (_stringAnimStep == 12) {
        _stringModelA = mdlLoadStringModelC();
        _stringVAOName = [self buildVAO:_stringModelA];
        
        _stringNumElements = _stringModelA->numElements;
        _stringPrimType = _stringModelA->primType;
        _stringElementType = _stringModelA->elementType;
        
        mdlDestroyModel(_stringModelA);
        _stringModelA = NULL;
    }
    if (_stringAnimStep == 18) {
        _stringAnimStep = -1;
    }

Now I realize this is crude... but I think it helped me understand the process a bit better. I think I would prefer to make a single call to draw my Vertex Array Object in the render loop and just update the relevant vertices in the Vertex Buffer Object. The class would handle updating the vertex data and animation.

Any insights on the best way to update my vertex data in the render loop?

Thanks again guys for any tips.


RE: Dissecting Apple's GLEssentials Demo - dockode - Oct 5, 2011 10:11 PM

Just an update. I did manage to get some very rough animation going by now updating only the VBO. Still not sure if that is the best way to go about things. Ponder As I have the principle in mind now I thought I'd move on to other things. So today I spent 5 hours (of misery and pain) Cry and managed to write an wavefront object loader for the GLEssentials demo. Wow Mind you the code is hideous but it works. Blush

Here is a screenshot of my progress with a quick model made by a friend. I'm using a shader I found described here:
Interpolated Color Shader

[Image: th_ScreenShot2011-10-06at125258AM.png]

And an earlier version before the loader.
[Image: th_ScreenShot2011-10-04at123806AM.png]


RE: Dissecting Apple's GLEssentials Demo - ipeku - Oct 16, 2011 04:22 PM

(Sep 26, 2011 06:31 PM)dockode Wrote:  ipeku - True but doesn't dragging the NSOpenGLView into your NSView and then changing the class of the NSOpenGLView to NSGLView link it to your functions?

By the way thank you both for your answers. I'm beginning to understand the way IB works a little better. Smile

I think the difference is only in IB. You can have that NSView or NSOpenGLView pointing to any subclass of NSOpenGLView in your code (because both are NSViews). However, if you use an NSOpenGLView in IB you will have some additional options there that you would otherwise have to set programmatically.