示例#1
0
void game_init(int* argc, char** argv, const char* title) {
  glutInit(argc, argv);
  glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
  glutInitWindowSize(800, 600);
  glutInitContextVersion(3, 1);
  glutInitContextProfile(GLUT_FORWARD_COMPATIBLE);
  glutInitContextProfile(GLUT_CORE_PROFILE);
  glutCreateWindow(title);
  glewExperimental = true;
  glewInit();

  glutKeyboardFunc(keyboard_down);
  glutKeyboardUpFunc(keyboard_up);
  glutSpecialFunc(keyboard_special_down);
  glutSpecialUpFunc(keyboard_special_up);
  glutCloseFunc(window_close);

  glutSetKeyRepeat(GLUT_KEY_REPEAT_OFF);
  glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

  bodies = list_new();
  body_program = pipeline_new(
    shader_new(SHADER_VERTEX, "body.vert"),
    shader_new(SHADER_FRAGMENT, "body.frag"));

  glGenVertexArrays(1, &body_vao);
  glBindVertexArray(body_vao);

  pipeline_attribute(body_program, "coord", 0);
  pipeline_attribute(body_program, "color", 1);
}
示例#2
0
void game_init(GameState* gs) {

  // Bind tiles vbo
  glGenBuffers(1, &gs->tiles_vbo);
  glBindBuffer(GL_ARRAY_BUFFER, gs->tiles_vbo);
  glBufferData(GL_ARRAY_BUFFER, sizeof(TILES_VBO)*sizeof(float), TILES_VBO, GL_STATIC_DRAW);

  gs->tiles_sheet = bitmap_load("bmp/tiles.bmp");
  gs->tiles_program = pipeline_new(
    shader_new(SHADER_VERTEX, "shader/tiles.vert"),
    shader_new(SHADER_FRAGMENT, "shader/tiles.frag"));

  pipeline_attribute(gs->tiles_program, "coord", 0);
  pipeline_attribute(gs->tiles_program, "st", 1);
  pipeline_uniform(gs->tiles_program, "pos", 0);
  pipeline_uniform(gs->tiles_program, "tex", 1);
  pipeline_uniform(gs->tiles_program, "tile", 2);

  // Bind player vbo
  glGenBuffers(1, &gs->player_vbo);
  glBindBuffer(GL_ARRAY_BUFFER, gs->player_vbo);
  glBufferData(GL_ARRAY_BUFFER, sizeof(PLAYER_VBO)*sizeof(float), PLAYER_VBO, GL_STATIC_DRAW);

  // load player spritesheet
  gs->player_spritesheet = bitmap_load("bmp/player.bmp");
  gs->player_x = gs->player_y = 0;
  gs->player_vx = gs->player_vy = 0;

  // create player render pipeline
  gs->player_program = pipeline_new(
    shader_new(SHADER_VERTEX, "shader/player.vert"),
    shader_new(SHADER_FRAGMENT, "shader/player.frag"));

  pipeline_attribute(gs->player_program, "coord", 0);
  pipeline_attribute(gs->player_program, "st", 1);
  pipeline_uniform(gs->player_program, "pos", 0);
  pipeline_uniform(gs->player_program, "tex", 1);
  pipeline_uniform(gs->player_program, "dir", 2);

  // Load audio stuff
  gs->bounce_sound = sound_load("sound/bounce.ogg");
  gs->ouch_sound = sound_load("sound/ouch.ogg");
  alGenSources(1, &gs->player_source);

  // Entity stuff
  gs->num_entities = 1;
  gs->ent[0].vbo = gs->player_vbo;
  gs->ent[0].spritesheet = gs->player_spritesheet;
  gs->ent[0].frame = 0;
  gs->ent[0].vx = gs->ent[0].vy = 8;
  gs->ent[0].x = gs->ent[0].y = 8;
  gs->ent[0].program = gs->player_program;
}
示例#3
0
文件: render.c 项目: rikxqd/seal2d
struct render* render_new() {
    struct render* r = STRUCT_NEW(render);
    r->cur_program = 0;
    r->cur_tex_id = 0;
    r->vertex_buffer = vertex_buffer_new();
    r->render_state = 0;
    r->shader = shader_new();
    r->scissors.x = 0;
    r->scissors.y = 0;
    r->scissors.width = GAME->config.window_width;
    r->scissors.width = GAME->config.window_height;
    r->drawcall = 0;
    return r;
}
示例#4
0
int main(int argc, char* argv[])
{
    int cw = 750;
    int ch = 750;

    cg_init(cw, ch, NULL);

    glClearColor(0,0,1,1);
    // Actualizar la pantalla:
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glMatrixMode(GL_PROJECTION);
    glViewport(0,0,cw, ch);
    glFrustum(-1,1,-1,1,1,1000);

    glEnable(GL_LIGHTING);

    // glEnable(GL_NORMALIZE);
    float l0[] = {1.0f,1.0f,1.0f,1.0f};
    float la[] = {0.10f,0.10f,0.10f,1.0f};
    float l0p[]= {1.0f,1.0f,1.0f,1.0f};
    float ls[] = {1.0f,1.0f,1.0f,1.0f};
    glLightfv(GL_LIGHT0, GL_AMBIENT, la);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, l0);
    glLightfv(GL_LIGHT0, GL_POSITION, l0p);
    glLightfv(GL_LIGHT0, GL_SPECULAR, ls);
    glEnable(GL_LIGHT0);

    float cyan[] = {1.0f, 1.0f, 1.0f, 1.f};
    glMaterialfv(GL_FRONT, GL_DIFFUSE, cyan);
    glMaterialfv(GL_FRONT, GL_SPECULAR, ls);
    glMateriali(GL_FRONT, GL_SHININESS, 16);

    float ang = 0.0f;
    float pitch = 0.0f;
    float ang_vel = 1.0f;


    char done = 0;
    char wireframe = 0;
    char bfc = 0;
    glEnable(GL_DEPTH_TEST);
    char zbuff = 1;
    unsigned char key_pressed[1024];
    memset(key_pressed, 0, 1024);

    char use_shader = 0;
    char specular = 0;
    Shader gouraud = shader_new("shaders/gouraud_vp.glsl", "shaders/gouraud_fp.glsl");
    GLint uniform_especular = shader_get_unif_loc(gouraud, "especular");
    GLint uniform_tex = shader_get_unif_loc(gouraud, "tex");

    Obj* objPtr ;
    int example = atoi(argv[1]);

    if (example==1 || example==3)
        objPtr = obj_load("../Models/knight_texturas.obj");
    else if (example==2)
        objPtr = obj_load("../Models/box_texturas.obj");

    //Cargo la imagen de disco usando SDL_image
    SDL_Surface* surface; // = IMG_Load("../Models/knight.png");

    if (example==1 )
        surface = IMG_Load("../Models/knight.png");
    else if (example==2)
        surface = IMG_Load("../Models/box.jpg");
    else if (example==3)
        surface = IMG_Load("../Models/knight_good.png");


//    Obj* objPtr = obj_load("../Models/knight_texturas.obj");
//    Obj* objPtr = obj_load("../Models/box_texturas.obj");

    //Cargo la imagen de disco usando SDL_image
//    SDL_Surface* surface = IMG_Load("../Models/knight.png");
//    SDL_Surface* surface = IMG_Load("../Models/knight_good.png");
//    SDL_Surface* surface = IMG_Load("../Models/box.jpg");

    if (surface==NULL) { //Si falla la carga de la imagen, despliego el mensaje de error correspondiente y termino el programa.
        printf("Error: \"%s\"\n", SDL_GetError());
        return 1;
    }

    GLuint texture;
    //Creo un espacio para alojar una textura en memoria de video
    glGenTextures(1,&texture);
    //Activo la textura nro 0
    glActiveTexture(GL_TEXTURE0);
    //Habilito la carga para la textura recien creada
    glBindTexture(GL_TEXTURE_2D,texture);

    //Cargo los datos de la imagen en la textura.
    glTexImage2D(GL_TEXTURE_2D,
                 0,
                 GL_RGBA,
                 surface->w,
                 surface->h,
                 0,
                 GL_RGB,GL_UNSIGNED_BYTE,
                 surface->pixels);
    //Luego de copiada la imagen a memoria de video, puedo liberarla sin problemas
    SDL_FreeSurface(surface);

    //Seteo el filtro a usar cuando se mapea la textura a una superficie mas chica (GL_LINEAR = filtro bilineal)
    glTexParameteri(GL_TEXTURE_2D,
                    GL_TEXTURE_MIN_FILTER,
                    GL_LINEAR);
    //Seteo el filtro a usar cuando se mapea la textura a una superficie mas grande (GL_LINEAR = filtro bilineal)
    glTexParameteri(GL_TEXTURE_2D,
                    GL_TEXTURE_MAG_FILTER,
                    GL_LINEAR);

    //Seteo el comportamiento cuando encuentro coordenadas de textura fuera del rango [0,1]
    //GL_REPEAT es el comportamiento por defecto.
    glTexParameteri(GL_TEXTURE_2D,
                    GL_TEXTURE_WRAP_S,
                    GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D,
                    GL_TEXTURE_WRAP_T,
                    GL_REPEAT);

    while (!done)
    {

        SDL_Event event;
        while(SDL_PollEvent(&event))
        {
            switch (event.type)
            {
            case SDL_KEYDOWN:
                key_pressed[event.key.keysym.sym] = 1;
                if (event.key.keysym.sym == SDLK_p)
                {
                    use_shader = !use_shader;
                    break;
                }
                else if (event.key.keysym.sym == SDLK_s)
                {
                    specular = !specular;
                    break;
                }
                else if (event.key.keysym.sym == SDLK_z)
                {
                    zbuff = !zbuff;
                    if(zbuff)
                        glEnable(GL_DEPTH_TEST);
                    else
                        glDisable(GL_DEPTH_TEST);
                    break;
                }
                else if (event.key.keysym.sym == SDLK_m)
                {
                    wireframe = !wireframe;
                    if(wireframe)
                        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
                    else
                        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
                    break;
                }
                else if (event.key.keysym.sym == SDLK_b)
                {
                    bfc = !bfc;
                    if(bfc)
                    {
                        glEnable(GL_CULL_FACE);
                        glCullFace(GL_BACK);
                        glFrontFace(GL_CW);
                    }
                    else
                        glDisable(GL_CULL_FACE);
                    break;
                }
                else if (event.key.keysym.sym != SDLK_ESCAPE)
                    break;
            case SDL_QUIT :
                done = 1;
                break;
            case SDL_KEYUP:
                key_pressed[event.key.keysym.sym] = 0;

            }
        }

        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();

        if (example==1 || example==3)
            glTranslatef(0.0f, 0.0f, -50.0f);
        else if (example==2)
            glTranslatef(0.0f, 0.0f, -2.0f);

//        glTranslatef(0.0f, 0.0f, -50.0f);
//        glTranslatef(0.0f, 0.0f, -2.0f);

        glRotatef(pitch, 1.0f, 0.0f, 0.0f);
        glRotatef(ang, 0.0f, 1.0f, 0.0f);

        glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);

        if(key_pressed[SDLK_RIGHT]) ang += ang_vel;
        if(key_pressed[SDLK_LEFT]) ang -= ang_vel;
        if(key_pressed[SDLK_UP]) pitch += ang_vel;
        if(key_pressed[SDLK_DOWN]) pitch -= ang_vel;

        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

        //Informo a OpenGL que para todas las operaciones a continuaci�n utilice las texturas 2D cargadas
        glEnable(GL_TEXTURE_2D);

        if(use_shader)
        {
            shader_use(gouraud);
            glUniform1i(uniform_especular, specular);
            //Le digo al shader que el sampler2D de nombre "tex" se corresponde con GL_TEXTURE0
            //que es donde cargu� mi textura.
            glUniform1i(uniform_tex, 0);
            //Luego asocio la textura con el id "texture"
            glBindTexture(GL_TEXTURE_2D,texture);
            obj_render2(objPtr);
            shader_stop(gouraud);
        }
        else
        {
            glBindTexture(GL_TEXTURE_2D,texture);
            obj_render2(objPtr);
        }

        cg_repaint();
    } // while

    // Liberar recursos:
    obj_free(objPtr);
    shader_free(gouraud);
    glDeleteTextures(1,&texture);
    cg_close();

    return 0;
}
示例#5
0
void boot_init(int* argc, char** argv) {

  int error;

  fail_if(glfwInit() == false);

  // OpenGL version 3
  glfwOpenWindowHint(GLFW_OPENGL_VERSION_MAJOR, 3);
  glfwOpenWindowHint(GLFW_OPENGL_VERSION_MINOR, 0);

  // No window resize
  glfwOpenWindowHint(GLFW_WINDOW_NO_RESIZE, true);
  // 400% gameboy scale, 5 bit rgb with 1 bit alpha, 8 bit z buffer
  fail_if(glfwOpenWindow(160 * 4, 144 * 4, 5, 5, 5, 1, 8, 0, GLFW_WINDOW) == false);

  glfwSetWindowTitle("");

  glfwEnable(GLFW_AUTO_POLL_EVENTS); // Automatically poll input on swap
  glfwSwapInterval(1); // 0: vsync off, 1: vsync on 

  printf("Renderer: %s\n", glGetString(GL_RENDERER));
  printf("GL Version: %s\n", glGetString(GL_VERSION));
  printf("Using GLSL: %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION));

  error = glewInit();

  if (error != GLEW_OK) {
    fprintf(stderr, "GLEW error: %s\n", glewGetErrorString(error));
  }

  printf("GL Extension Wrangler: %s\n", glewGetString(GLEW_VERSION));

  // Load audio
  fail_if(alutInit(argc, argv) == false);

  glClearColor(206.0/255.0, 230.0/255.0, 165.0/255.0, 1.0f);
  glEnable(GL_SCISSOR_TEST); // For geom culling
  
  // transparency
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  // z-buffer
  glEnable(GL_DEPTH_TEST);
  glDepthFunc(GL_LEQUAL);

  // Set up perspective
  glOrtho(0, 160, 144, 0, -1.0, 1.0);
  glDepthRange(-1.0f, 1.0f);
  glViewport(0, 0, 160 * 4, 144 * 4);

  // This is where Ill put the logo thing
  unsigned logo = bitmap_load("bmp/logo.bmp");
  unsigned logo_vbo;
  float t0, t1, dt;
  float logo_x = 16, logo_y = -80;
  float logo_v[] = {
    0,     0, 0, 1.0 - 0,
    0,   128, 0, 1.0 - 1,
    128, 128, 1, 1.0 - 1,
    128,   0, 1, 1.0 - 0,
  };

  glGenBuffers(1, &logo_vbo);
  glBindBuffer(GL_ARRAY_BUFFER, logo_vbo);
  glBufferData(GL_ARRAY_BUFFER, sizeof(logo_v)*sizeof(float), logo_v, GL_STATIC_DRAW);
 
  Pipeline* logo_program = pipeline_new(
    shader_new(SHADER_VERTEX, "shader/logo.vert"),
    shader_new(SHADER_FRAGMENT, "shader/logo.frag"));

  pipeline_attribute(logo_program, "coord", 0);
  pipeline_attribute(logo_program, "st", 1);
  pipeline_uniform(logo_program, "pos", 0);
  pipeline_uniform(logo_program, "tex", 1);

  glUseProgram(logo_program->id);

  // Bind the logo to texture 0
  glActiveTexture(GL_TEXTURE0);
  glBindTexture(GL_TEXTURE_2D, logo);
  glUniform1i(logo_program->uniform[1], 0);

  // Bind logo vbo
  glBindBuffer(GL_ARRAY_BUFFER, logo_vbo);
  glEnableVertexAttribArray(logo_program->attribute[0]);
  glVertexAttribPointer(logo_program->attribute[0], 2, GL_FLOAT, false, 4*sizeof(float), (void*)0);
  glEnableVertexAttribArray(logo_program->attribute[1]);
  glVertexAttribPointer(logo_program->attribute[1], 2, GL_FLOAT, false, 4*sizeof(float), (void*)(2 * sizeof(float)));

  // load sound
  unsigned sound_source;
  alGenSources(1, &sound_source);
  Sound* ding = sound_load("sound/ding.ogg");

  alSourcei(sound_source, AL_BUFFER, ding->buffer);
  
  glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
  glfwSwapBuffers();

  sleep(1);

  t0 = glfwGetTime();
  while (logo_y < 8) {
    t1 = glfwGetTime();
    dt = t1 - t0;
    t0 = t1;

    logo_y += 50 * dt;

    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

    glUniform2f(logo_program->uniform[0], roundf(logo_x), roundf(logo_y));

    // Render logo
    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

    glfwSwapBuffers();
  }

  alSourcePlay(sound_source);

  glDisableVertexAttribArray(logo_program->attribute[0]);
  glDisableVertexAttribArray(logo_program->attribute[1]);

  sleep(1);
}
示例#6
0
文件: main.c 项目: zakkor/renderer
// The MAIN function, from here we start the application and run the game loop
int main()
{
    // Init GLFW
    glfwInit();
    // Set all the required options for GLFW
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    // Create a GLFWwindow object that we can use for GLFW's functions
    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", NULL, NULL);
    glfwMakeContextCurrent(window);

    // Set the required callback functions
    glfwSetKeyCallback(window, key_callback);

    // Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensions
    glewExperimental = GL_TRUE;
    // Initialize GLEW to setup the OpenGL Function pointers
    glewInit();

    // Define the viewport dimensions
    glViewport(0, 0, WIDTH, HEIGHT);

    // Setup OpenGL options
    glEnable(GL_DEPTH_TEST);


    // Build and compile our shader program
    shader our_shader = shader_new("shader.vs", "shader.frag");


    // Set up vertex data (and buffer(s)) and attribute pointers
    GLfloat vertices[] = {
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
         0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,

        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,

        -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  1.0f, 1.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,

        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f
    };
    // World space positions of our cubes
    graphene_point3d_t cubePositions[] = {
        GRAPHENE_POINT3D_INIT(0.0f,  0.0f,  0.0f),
        GRAPHENE_POINT3D_INIT( 2.0f,  5.0f, -15.0f),
        GRAPHENE_POINT3D_INIT(-1.5f, -2.2f, -2.5f),
        GRAPHENE_POINT3D_INIT(-3.8f, -2.0f, -12.3f),
        GRAPHENE_POINT3D_INIT( 2.4f, -0.4f, -3.5f),
        GRAPHENE_POINT3D_INIT(-1.7f,  3.0f, -7.5f),
        GRAPHENE_POINT3D_INIT( 1.3f, -2.0f, -2.5f),
        GRAPHENE_POINT3D_INIT( 1.5f,  2.0f, -2.5f),
        GRAPHENE_POINT3D_INIT( 1.5f,  0.2f, -1.5f),
        GRAPHENE_POINT3D_INIT(-1.3f,  1.0f, -1.5f)
    };

    GLuint VBO, VAO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);

    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // Position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    // TexCoord attribute
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(2);

    glBindVertexArray(0); // Unbind VAO


    // Load and create a texture
    GLuint texture1;
    GLuint texture2;
    // ====================
    // Texture 1
    // ====================
    glGenTextures(1, &texture1);
    glBindTexture(GL_TEXTURE_2D, texture1); // All upcoming GL_TEXTURE_2D operations now have effect on our texture object
    // Set our texture parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);	// Set texture wrapping to GL_REPEAT
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // Set texture filtering
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // Load, create texture and generate mipmaps
    int width, height;
    unsigned char* image = SOIL_load_image("container.jpg", &width, &height, 0, SOIL_LOAD_RGB);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);
    SOIL_free_image_data(image);
    glBindTexture(GL_TEXTURE_2D, 0); // Unbind texture when done, so we won't accidentily mess up our texture.
    // ===================
    // Texture 2
    // ===================
    glGenTextures(1, &texture2);
    glBindTexture(GL_TEXTURE_2D, texture2);
    // Set our texture parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // Set texture filtering
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // Load, create texture and generate mipmaps
    image = SOIL_load_image("awesomeface.png", &width, &height, 0, SOIL_LOAD_RGB);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);
    SOIL_free_image_data(image);
    glBindTexture(GL_TEXTURE_2D, 0);


    // Game loop
    while (!glfwWindowShouldClose(window))
    {
        // Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functions
        glfwPollEvents();

        // Render
        // Clear the colorbuffer
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


        // Bind Textures using texture units
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texture1);
        glUniform1i(glGetUniformLocation(our_shader.program, "ourTexture1"), 0);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, texture2);
        glUniform1i(glGetUniformLocation(our_shader.program, "ourTexture2"), 1);

        // Activate shader
        shader_use(&our_shader);

        // Create transformations
        graphene_matrix_t view;
        graphene_point3d_t view_point;
        graphene_matrix_init_translate(&view, graphene_point3d_init(&view_point, 0.0f, 0.0f, -3.0f));

        graphene_matrix_t projection;
        graphene_matrix_init_perspective(&projection, 45.0f, (GLfloat) WIDTH / (GLfloat) HEIGHT, 0.1f, 100.0f);

        // Get their uniform location
        GLint modelLoc = glGetUniformLocation(our_shader.program, "model");
        GLint viewLoc = glGetUniformLocation(our_shader.program, "view");
        GLint projLoc = glGetUniformLocation(our_shader.program, "projection");

        // Pass the matrices to the shader
        GLfloat view_f[16] = {};
        graphene_matrix_to_float(&view, view_f);
        GLfloat proj_f[16] = {};
        graphene_matrix_to_float(&projection, proj_f);

        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, view_f);
        // Note: currently we set the projection matrix each frame, but since the projection matrix rarely changes it's often best practice to set it outside the main loop only once.
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, proj_f);

        glBindVertexArray(VAO);
        for (GLuint i = 0; i < 10; i++)
        {
            // Calculate the model matrix for each object and pass it to shader before drawing
            graphene_matrix_t model;
            graphene_matrix_init_translate(&model, &cubePositions[i]);
            GLfloat angle = 5.0f * i;

            graphene_vec3_t axis;
            graphene_vec3_init(&axis, -1.3f,  1.0f, -1.5f);
            graphene_matrix_rotate(&model, angle, &axis);

            GLfloat model_f[16] = {};
            graphene_matrix_to_float(&model, model_f);

            glUniformMatrix4fv(modelLoc, 1, GL_FALSE, model_f);

            glDrawArrays(GL_TRIANGLES, 0, 36);
        }
        glBindVertexArray(0);

        // Swap the screen buffers
        glfwSwapBuffers(window);
    }
    // Properly de-allocate all resources once they've outlived their purpose
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    // Terminate GLFW, clearing any resources allocated by GLFW.
    glfwTerminate();
    return 0;
}