The missing triangle.

Started by
5 comments, last by 21st Century Moose 6 years, 9 months ago

I have just "finished" learning OpenGL + glut.  

I would like to try my hand at not using glut to try and get a better understanding of what goes on under the hood etc. I have been having great difficulty with the following code. I am trying to learn from a post on GitHub https://github.com/gamedevtech/X11OpenGLWindow and the example towards the end works but the triangle that it is suppose to render, does not. I thought it was because glBegin()--glEnd() are no more in current version of OpenGL. I tried to rewrite it using glDrawArrays. It did not work in the first place, what stupid thing am I doing here?


#include <iostream>
#include <cstring>

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysymdef.h>

#include <GL/gl.h>
#include <GL/glx.h>

#include <sys/time.h>
#include <unistd.h>

#define WINDOW_WIDTH    800
#define WINDOW_HEIGHT    600
#define FPS 60
#define TEST_LOCAL

extern bool Initialize(int w, int h);
extern bool Update(float deltaTime);
extern void Render();
extern void Resize(int w, int h);
extern void Shutdown();
static void drawTriangle(void);
typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);

#define SKIP_TICKS      (1000 / FPS)

static double GetMilliseconds() {
    static timeval s_tTimeVal;
    gettimeofday(&s_tTimeVal, NULL);
    double time = s_tTimeVal.tv_sec * 1000.0; // sec to ms
    time += s_tTimeVal.tv_usec / 1000.0; // us to ms
    return time;
}

static bool isExtensionSupported(const char *extList, const char *extension) {
    return strstr(extList, extension) != 0;
}


int main(int argc, char** argv) {
    Display* display;
    Window window;
    Screen* screen;
    int screenId;
    XEvent ev;

    // Open the display
    display = XOpenDisplay(NULL);
    if (display == NULL) {
        std::cout << "Could not open display\n";
        return 1;
    }
    screen = DefaultScreenOfDisplay(display);
    screenId = DefaultScreen(display);

    // Check GLX version
    GLint majorGLX, minorGLX = 0;
    glXQueryVersion(display, &majorGLX, &minorGLX);
    if (majorGLX <= 1 && minorGLX < 2) {
        std::cout << "GLX 1.2 or greater is required.\n";
        XCloseDisplay(display);
        return 1;
    }

    GLint glxAttribs[] = {
        GLX_X_RENDERABLE    , True,
        GLX_DRAWABLE_TYPE   , GLX_WINDOW_BIT,
        GLX_RENDER_TYPE     , GLX_RGBA_BIT,
        GLX_X_VISUAL_TYPE   , GLX_TRUE_COLOR,
        GLX_RED_SIZE        , 8,
        GLX_GREEN_SIZE      , 8,
        GLX_BLUE_SIZE       , 8,
        GLX_ALPHA_SIZE      , 8,
        GLX_DEPTH_SIZE      , 24,
        GLX_STENCIL_SIZE    , 8,
        GLX_DOUBLEBUFFER    , True,
        None
    };

    int fbcount;
    GLXFBConfig* fbc = glXChooseFBConfig(display, screenId, glxAttribs, &fbcount);
    if (fbc == 0) {
        std::cout << "Failed to retrieve framebuffer.\n";
        XCloseDisplay(display);
        return 1;
    }

    // Pick the FB config/visual with the most samples per pixel
    int best_fbc = -1, worst_fbc = -1, best_num_samp = -1, worst_num_samp = 999;
    for (int i = 0; i < fbcount; ++i) {
        XVisualInfo *vi = glXGetVisualFromFBConfig( display, fbc );
        if ( vi != 0)
        {
            int samp_buf, samples;
            glXGetFBConfigAttrib( display, fbc, GLX_SAMPLE_BUFFERS, &samp_buf );
            glXGetFBConfigAttrib( display, fbc, GLX_SAMPLES       , &samples  );

            if ( best_fbc < 0 || (samp_buf && samples > best_num_samp) ) {
                best_fbc = i;
                best_num_samp = samples;
            }
            if ( worst_fbc < 0 || !samp_buf || samples < worst_num_samp )
                worst_fbc = i;
            worst_num_samp = samples;
        }
        XFree( vi );
    }
    GLXFBConfig bestFbc = fbc[ best_fbc ];
    XFree( fbc ); // Make sure to free this!

    XVisualInfo* visual = glXGetVisualFromFBConfig( display, bestFbc );
    if (visual == 0) {
        std::cout << "Could not create correct visual window.\n";
        XCloseDisplay(display);
        return 1;
    }

    if (screenId != visual->screen) {
        std::cout << "screenId(" << screenId << ") does not match visual->screen(" << visual->screen << ").\n";
        XCloseDisplay(display);
        return 1;

    }

    // Open the window
    XSetWindowAttributes windowAttribs;
    windowAttribs.border_pixel = BlackPixel(display, screenId);
    windowAttribs.background_pixel = WhitePixel(display, screenId);
    windowAttribs.override_redirect = True;
    windowAttribs.colormap = XCreateColormap(display, RootWindow(display, screenId), visual->visual, AllocNone);
    windowAttribs.event_mask = ExposureMask;
    window = XCreateWindow(display, RootWindow(display, screenId), 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, visual->depth, InputOutput, visual->visual, CWBackPixel | CWColormap | CWBorderPixel | CWEventMask, &windowAttribs);

    // Redirect Close
    Atom atomWmDeleteWindow = XInternAtom(display, "WM_DELETE_WINDOW", False);
    XSetWMProtocols(display, window, &atomWmDeleteWindow, 1);

    // Create GLX OpenGL context
    glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;
    glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) glXGetProcAddressARB( (const GLubyte *) "glXCreateContextAttribsARB" );

    int context_attribs[] = {
        GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
        GLX_CONTEXT_MINOR_VERSION_ARB, 2,
        GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
        None
    };

    GLXContext context = 0;
    const char *glxExts = glXQueryExtensionsString( display,  screenId );
    if (!isExtensionSupported( glxExts, "GLX_ARB_create_context")) {
        std::cout << "GLX_ARB_create_context not supported\n";
        context = glXCreateNewContext( display, bestFbc, GLX_RGBA_TYPE, 0, True );
    }
    else {
        context = glXCreateContextAttribsARB( display, bestFbc, 0, true, context_attribs );
    }
    XSync( display, False );

    // Verifying that context is a direct context
    if (!glXIsDirect (display, context)) {
        std::cout << "Indirect GLX rendering context obtained\n";
    }
    else {
        std::cout << "Direct GLX rendering context obtained\n";
    }
    glXMakeCurrent(display, window, context);

    std::cout << "GL Renderer: " << glGetString(GL_RENDERER) << "\n";
    std::cout << "GL Version: " << glGetString(GL_VERSION) << "\n";
    std::cout << "GLSL Version: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << "\n";

    if (!Initialize(WINDOW_WIDTH, WINDOW_HEIGHT)) {
        glXDestroyContext(display, context);
        XFree(visual);
        XFreeColormap(display, windowAttribs.colormap);
        XDestroyWindow(display, window);
        XCloseDisplay(display);
        return 1;
    }

    // Show the window
    XClearWindow(display, window);
    XMapRaised(display, window);

    double prevTime = GetMilliseconds();
    double currentTime = GetMilliseconds();
    double deltaTime = 0.0;

    timeval time;
    long sleepTime = 0;
    gettimeofday(&time, NULL);
    long nextGameTick = (time.tv_sec * 1000) + (time.tv_usec / 1000);

    // Enter message loop
    while (true) {
        if (XPending(display) > 0) {
            XNextEvent(display, &ev);
            if (ev.type == Expose) {
                XWindowAttributes attribs;
                XGetWindowAttributes(display, window, &attribs);
                Resize(attribs.width, attribs.height);
            }
            if (ev.type == ClientMessage) {
                if (ev.xclient.data.l[0] == atomWmDeleteWindow) {
                    break;
                }
            }
            else if (ev.type == DestroyNotify) {
                break;
            }
        }

        currentTime = GetMilliseconds();
        deltaTime = double(currentTime - prevTime) * 0.001;
        prevTime = currentTime;

        if (!Update((float)deltaTime)) {
            break;
        }


        Render();

        // Present frame
        glXSwapBuffers(display, window);

        // Limit Framerate
        gettimeofday(&time, NULL);
        nextGameTick += SKIP_TICKS;
        sleepTime = nextGameTick - ((time.tv_sec * 1000) + (time.tv_usec / 1000));
        usleep((unsigned int)(sleepTime / 1000));
    }

    std::cout << "Shutting Down\n";
    Shutdown();

    // Cleanup GLX
    glXDestroyContext(display, context);

    // Cleanup X11
    XFree(visual);
    XFreeColormap(display, windowAttribs.colormap);
    XDestroyWindow(display, window);
    XCloseDisplay(display);
    return 0;
}

static float vertices2AndColors2Intertwined[] =
{
        0.0, 0.0, 0.0, 0.0, 1.0, 1.0,
    10.0,0.0, 0.0, 1.0, 0.0, 0.0,
    10.0, 10.0, 0.0, 0.0, 1.0, 0.0
};

static void drawTriangle(void)
{
    glVertexPointer(3, GL_FLOAT, 6*sizeof(float), &vertices2AndColors2Intertwined[0]);
    glColorPointer(3, GL_FLOAT, 6*sizeof(float), &vertices2AndColors2Intertwined[3]);
    glDrawArrays(GL_TRIANGLES, 0, 3);
}


#ifdef TEST_LOCAL

bool Initialize(int w, int h) {
    glClearColor(0.5f, 0.6f, 0.7f, 1.0f);
    glViewport(0, 0, w, h);
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);
    return true;
}
bool Update(float deltaTime) {
    return true;
}

void Render()
{
    glClear(GL_COLOR_BUFFER_BIT);
    drawTriangle();
}
void Resize(int w, int h) {
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0.0, 100.0, 0.0, 100.0, -1.0, 1.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void Shutdown() {

}
#endif 

 

Advertisement

I know the article you referenced mentions that they wish to use Xwindow and makes points of how easy it is, but could I just suggest you use something like glfw if your main aim is to just do graphics programming - and once you get used to it you could switch back to X if you wish.

Your above code also seems to mix legacy OpenGL and modern opengl quite a bit, and commenting on what parts are wrong would take more time then i have at the minute, can i suggest reading through learnopengl.com It is a great starting point and talks through most if not all issues you are seeing - even if you want to get your own program running and carry on with those tutorials - just have a look at the hello triangle page, and it will show you how to correctly set up your buffers and render to the screen

2 hours ago, McGrane said:

Your above code also seems to mix legacy OpenGL and modern opengl quite a bit

It's actually all legacy; GL 1.1 using client-side vertex arrays and fixed function vertex attribs to be precise.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

Some things you can try:

 

See this wiki for how to create X11 windows with GL 3 capabilities.

Check all X11 errors.

Choose for example a red clear color. Clear your buffer. Does it become red ? If so, then you might be doing it well for creating your window with a GL context.

Avoid interlaced arrays if it's your first time with them.

To to stick back to a GL 2.x context and use glBegin/glEnd. These functions are less error prone. For how to create an 'old' context, see this wiki page for example. And once you have something, move to the new context creation.

Thanks for the reply's. I will check out the links. I wanted to try and learn the mysterious "raw" openGL, but it seems like an impossible task! every thing I can find seems to use GLEW or GLUT etc. Just to see how each of the individual steps work.

There is no "raw" OpenGL.

The task of creating a context, irrespective of platform, has never been part of the OpenGL API, but is instead delegated to the operating system. By learning context creation all you are learning is a few OS-level calls with the appropriate flags. This is of absolutely no benefit to your eventual usage of OpenGL itself.

Regarding GL versions, you'll sometimes see buffers and shaders discussed as though they were modern features. This is completely untrue. Vertex arrays date back to OpenGL 1.1, buffer objects to 1.5 and shaders to 2.0, and all are available as extensions to even earlier versions. These "modern" features are actually 15 years (or more) old. Don't be concerned about any risk of using them, these are mature, stable features.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

This topic is closed to new replies.

Advertisement