void multidrawindirect_app::render(double currentTime)
{
    int j;
    static const float one = 1.0f;
    static const float black[] = { 0.0f, 0.0f, 0.0f, 0.0f };

        
    static double last_time = 0.0;
    static double total_time = 0.0;

    if (!paused)
        total_time += (currentTime - last_time);
    last_time = currentTime;

    float t = float(total_time);
    int i = int(total_time * 3.0f);

    glViewport(0, 0, info.windowWidth, info.windowHeight);
    glClearBufferfv(GL_COLOR, 0, black);
    glClearBufferfv(GL_DEPTH, 0, &one);

    const vmath::mat4 view_matrix = vmath::lookat(vmath::vec3(100.0f * cosf(t * 0.023f), 100.0f * cosf(t * 0.023f), 300.0f * sinf(t * 0.037f) - 600.0f),
                                                  vmath::vec3(0.0f, 0.0f, 260.0f),
                                                  vmath::normalize(vmath::vec3(0.1f - cosf(t * 0.1f) * 0.3f, 1.0f, 0.0f)));
    const vmath::mat4 proj_matrix = vmath::perspective(50.0f,
                                                       (float)info.windowWidth / (float)info.windowHeight,
                                                       1.0f,
                                                       2000.0f);

    glUseProgram(render_program);

    glUniform1f(uniforms.time, t);
    glUniformMatrix4fv(uniforms.view_matrix, 1, GL_FALSE, view_matrix);
    glUniformMatrix4fv(uniforms.proj_matrix, 1, GL_FALSE, proj_matrix);
    glUniformMatrix4fv(uniforms.viewproj_matrix, 1, GL_FALSE, proj_matrix * view_matrix);

    glBindVertexArray(object.get_vao());

    if (mode == MODE_MULTIDRAW)
    {
        glMultiDrawArraysIndirect(GL_TRIANGLES, NULL, NUM_DRAWS, 0);
    }
    else if (mode == MODE_SEPARATE_DRAWS)
    {
        for (j = 0; j < NUM_DRAWS; j++)
        {
            GLuint first, count;
            object.get_sub_object_info(j % object.get_sub_object_count(), first, count);
            glDrawArraysInstancedBaseInstance(GL_TRIANGLES,
                                              first,
                                              count,
                                              1, j);
        }
    }
}
void blinnphong_app::startup()
{
    load_shaders();

    glGenBuffers(1, &uniforms_buffer);
    glBindBuffer(GL_UNIFORM_BUFFER, uniforms_buffer);
    glBufferData(GL_UNIFORM_BUFFER, sizeof(uniforms_block), NULL, GL_DYNAMIC_DRAW);

    object.load("media/objects/sphere.sbm");

    glEnable(GL_CULL_FACE);
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
}
void blinnphong_app::render(double currentTime)
{
    static const GLfloat zeros[] = { 0.0f, 0.0f, 0.0f, 0.0f };
    static const GLfloat gray[] = { 0.1f, 0.1f, 0.1f, 0.0f };
    static const GLfloat ones[] = { 1.0f };
    const float f = (float)currentTime;

    glUseProgram(per_fragment_program);
    glViewport(0, 0, info.windowWidth, info.windowHeight);

    glClearBufferfv(GL_COLOR, 0, gray);
    glClearBufferfv(GL_DEPTH, 0, ones);

    /*
    vmath::mat4 model_matrix = vmath::rotate((float)currentTime * 14.5f, 0.0f, 1.0f, 0.0f) *
                               vmath::rotate(180.0f, 0.0f, 0.0f, 1.0f) *
                               vmath::rotate(20.0f, 1.0f, 0.0f, 0.0f);
                               */

    vmath::vec3 view_position = vmath::vec3(0.0f, 0.0f, 50.0f);
    vmath::mat4 view_matrix = vmath::lookat(view_position,
                                            vmath::vec3(0.0f, 0.0f, 0.0f),
                                            vmath::vec3(0.0f, 1.0f, 0.0f));

    vmath::vec3 light_position = vmath::vec3(-20.0f, -20.0f, 0.0f);

    vmath::mat4 light_proj_matrix = vmath::frustum(-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 200.0f);
    vmath::mat4 light_view_matrix = vmath::lookat(light_position,
                                                  vmath::vec3(0.0f), vmath::vec3(0.0f, 1.0f, 0.0f));

#if defined(MANY_OBJECTS)
    int i, j;

    for (j = 0; j < 7; j++)
    {
        for (i = 0; i < 7; i++)
        {
            glBindBufferBase(GL_UNIFORM_BUFFER, 0, uniforms_buffer);
            uniforms_block * block = (uniforms_block *)glMapBufferRange(GL_UNIFORM_BUFFER,
                                                                        0,
                                                                        sizeof(uniforms_block),
                                                                        GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);

            vmath::mat4 model_matrix = vmath::translate((float)i * 2.75f - 8.25f, 6.75f - (float)j * 2.25f, 0.0f);

            block->mv_matrix = view_matrix * model_matrix;
            block->view_matrix = view_matrix;
            block->proj_matrix = vmath::perspective(50.0f,
                                                    (float)info.windowWidth / (float)info.windowHeight,
                                                    0.1f,
                                                    1000.0f);

            glUnmapBuffer(GL_UNIFORM_BUFFER);

            glUniform1f(uniforms[per_vertex ? 1 : 0].specular_power, powf(2.0f, (float)j + 2.0f));
            glUniform3fv(uniforms[per_vertex ? 1 : 0].specular_albedo, 1, vmath::vec3((float)i / 9.0f + 1.0f / 9.0f));

            object.render();
        }
    }
#else
    glBindBufferBase(GL_UNIFORM_BUFFER, 0, uniforms_buffer);
    uniforms_block * block = (uniforms_block *)glMapBufferRange(GL_UNIFORM_BUFFER,
                                                                0,
                                                                sizeof(uniforms_block),
                                                                GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);

    vmath::mat4 model_matrix = vmath::scale(7.0f);

    block->mv_matrix = view_matrix * model_matrix;
    block->view_matrix = view_matrix;
    block->proj_matrix = vmath::perspective(50.0f,
                                            (float)info.windowWidth / (float)info.windowHeight,
                                            0.1f,
                                            1000.0f);

    glUnmapBuffer(GL_UNIFORM_BUFFER);

    glUniform1f(uniforms[0].specular_power, 30.0f);
    glUniform3fv(uniforms[0].specular_albedo, 1, vmath::vec3(1.0f));

    object.render();
#endif
}
void multidrawindirect_app::startup()
{
    int i;

    load_shaders();

    object.load("media/objects/asteroids.sbm");

    glGenBuffers(1, &indirect_draw_buffer);
    glBindBuffer(GL_DRAW_INDIRECT_BUFFER, indirect_draw_buffer);
    glBufferData(GL_DRAW_INDIRECT_BUFFER,
                 NUM_DRAWS * sizeof(DrawArraysIndirectCommand),
                 NULL,
                 GL_STATIC_DRAW);

    DrawArraysIndirectCommand * cmd = (DrawArraysIndirectCommand *)
        glMapBufferRange(GL_DRAW_INDIRECT_BUFFER,
                         0,
                         NUM_DRAWS * sizeof(DrawArraysIndirectCommand),
                         GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);

    for (i = 0; i < NUM_DRAWS; i++)
    {
        object.get_sub_object_info(i % object.get_sub_object_count(),
                                   cmd[i].first,
                                   cmd[i].count);
        cmd[i].primCount = 1;
        cmd[i].baseInstance = i;
    }

    glUnmapBuffer(GL_DRAW_INDIRECT_BUFFER);

    glBindVertexArray(object.get_vao());

    glGenBuffers(1, &draw_index_buffer);
    glBindBuffer(GL_ARRAY_BUFFER, draw_index_buffer);
    glBufferData(GL_ARRAY_BUFFER,
                 NUM_DRAWS * sizeof(GLuint),
                 NULL,
                 GL_STATIC_DRAW);

    GLuint * draw_index =
        (GLuint *)glMapBufferRange(GL_ARRAY_BUFFER,
                                   0,
                                   NUM_DRAWS * sizeof(GLuint),
                                   GL_MAP_WRITE_BIT |
                                   GL_MAP_INVALIDATE_BUFFER_BIT);

    for (i = 0; i < NUM_DRAWS; i++)
    {
        draw_index[i] = i;
    }

    glUnmapBuffer(GL_ARRAY_BUFFER);

    glVertexAttribIPointer(10, 1, GL_UNSIGNED_INT, 0, NULL);
    glVertexAttribDivisor(10, 1);
    glEnableVertexAttribArray(10);

    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);

    glEnable(GL_CULL_FACE);
}