Note-drawing program












4












$begingroup$


This program draws a random music note on a staff, refreshing the note every second. It compiles with g++ and also emscripten, so it is runnable in both the browser and on desktop.



This is my first attempt at graphics programming, so I'm not interested in feedback on how I'm using OpenGL. In particular, I'm vaguely considered that I'm using shaders poorly. Do I really write a new shader program for each sort of thing I have to draw?



This code uses Bitmap.h, Bitmap.cpp, platform.hpp, and platform_linux.cpp by Thomas Dalling. These files assume that stb_image.h is located on the system include path. shader.cpp is modified from the shader code in this tutorial, but it's just standard shader-loading code. This project uses a unity build.



Makefile



all: a.out index.html

EMS_OPTS = -s FULL_ES3=1
-s WASM=1
-s NO_EXIT_RUNTIME=1
-s USE_GLFW=3
-s USE_LIBPNG=1
-DEMSCRIPTEN

CXX_FLAGS = -Wall -Wextra -std=c++17
LD_LFAGS = -lGL -lGLU -lglfw -lGLEW -lpng

index.html: main.cpp Drawable.cpp Drawable.h
emcc main.cpp -o index.html ${CXX_FLAGS} ${EMS_OPTS} --preload-file resources/whole-note.png

a.out: main.cpp Drawable.cpp Drawable.h
g++ -g main.cpp ${CXX_FLAGS} ${LD_LFAGS}


Drawable.h



#ifndef DRAWABLE_H_
#define DRAWABLE_H_

#include <chrono>
#include <memory>
#include <vector>

class Drawable
{
public:
virtual ~Drawable() {}
virtual void draw() const = 0;
virtual void update() {};
Drawable(const Drawable& other) = delete;
Drawable& operator=(const Drawable& other) = delete;
Drawable() {}
};

class Note : public Drawable
{
public:
Note(GLuint program);
~Note();
virtual void draw() const;

void setY(double value);
private:
GLuint vao, vbo, ibo, texture, program;
};

class Staff : public Drawable
{
public:
Staff(GLuint program, Note& note);
~Staff();
virtual void draw() const;
virtual void update();
private:
GLuint vao, vbo, program;
static const GLfloat points[30];
Note& note;
std::chrono::time_point<std::chrono::system_clock> start;
std::vector<float> valid_positions;
};

#endif


Drawable.cpp



#include <algorithm>
#include <random>
#include "Drawable.h"

namespace
{
GLfloat vertices = {
// X, Y, Z U, V
0.2, 0.0, 0.0, 1.0, 1.0,
-0.2, 0.0, 0.0, 0.0, 1.0,
0.2, 0.0, 0.0, 1.0, 0.0,
-0.2, 0.0, 0.0, 0.0, 0.0,
};
}

Note::Note(GLuint program) :
program(program)
{
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

// X, Y, Z (dest) coordinates
glVertexAttribPointer(0,
3,
GL_FLOAT,
GL_FALSE,
5 * sizeof(GL_FLOAT),
NULL);
glEnableVertexAttribArray(0);

// U, V (src) coordinates
glVertexAttribPointer(1,
2,
GL_FLOAT,
GL_FALSE,
5 * sizeof(GL_FLOAT),
(const GLvoid *)(3 * sizeof(GL_FLOAT)));
glEnableVertexAttribArray(1);

// TODO: Pass this in?
auto bmp = tdogl::Bitmap::bitmapFromFile(ResourcePath("whole-note.png"));
bmp.flipVertically();

// Index buffer object
glGenBuffers(1, &ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
static const GLuint indexData = {
0, 1, 2,
2, 1, 3
};
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indexData), indexData,
GL_STATIC_DRAW);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGBA,
(GLsizei)bmp.width(),
(GLsizei)bmp.height(),
0,
GL_RGBA,
GL_UNSIGNED_BYTE, bmp.pixelBuffer());
glBindTexture(GL_TEXTURE_2D, 0);
}

void Note::setY(double value)
{
static const float HALF_DIM = .2;
vertices[1] = value + HALF_DIM;
vertices[6] = value + HALF_DIM;
vertices[11] = value - HALF_DIM;
vertices[16] = value - HALF_DIM;

glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}

Note::~Note()
{
glDeleteProgram(program);
glDeleteBuffers(1, &vbo);
glDeleteBuffers(1, &ibo);
glDeleteVertexArrays(1, &vao);
glDeleteTextures(1, &texture);
}

void Note::draw() const
{
glUseProgram(program);
glBindVertexArray(vao);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);

GLint uniform = glGetUniformLocation(program, "texture_");
glUniform1i(uniform, 0);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

glBindVertexArray(0);
}

Staff::Staff(GLuint program, Note& note) :
program(program),
note(note),
start(std::chrono::system_clock::now()),
valid_positions{.5, .55, .6, .65, .7, .75, .8, .85}
{
note.setY(valid_positions[0]);
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);

glVertexAttribPointer(
0,
3,
GL_FLOAT,
GL_FALSE,
0,
(void*)0
);
glEnableVertexAttribArray(0);
glBindVertexArray(0);
}

Staff::~Staff()
{
glDeleteBuffers(1, &vbo);
glDeleteVertexArrays(1, &vao);
glDeleteProgram(program);
}

void Staff::draw() const
{
glUseProgram(program);
glBindVertexArray(vao);
glDrawArrays(GL_LINES, 0, 10);

note.draw();
}

const GLfloat Staff::points[30] = {
-1.0f, 0.9f, 0.0f,
1.0f, 0.9f, 0.0f,

-1.0f, 0.8f, 0.0f,
1.0f, 0.8f, 0.0f,

-1.0f, 0.7f, 0.0f,
1.0f, 0.7f, 0.0f,

-1.0f, 0.6f, 0.0f,
1.0f, 0.6f, 0.0f,

-1.0f, 0.5f, 0.0f,
1.0f, 0.5f, 0.0f,
};

void Staff::update()
{
const auto current_time = std::chrono::system_clock::now();
const auto duration = std::chrono::duration_cast<std::chrono::seconds>(current_time - start).count();
if (duration >= 1)
{
start = std::chrono::system_clock::now();

std::vector<float> new_position;
std::sample(valid_positions.begin(), valid_positions.end(),
std::back_inserter(new_position), 1,
std::mt19937{std::random_device{}()});
note.setY(new_position[0]);
}
}


main.cpp



#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vector>
#include <memory>

#ifdef EMSCRIPTEN
#include <GLES3/gl3.h>
#include "emscripten.h"
#else
#include <GL/glew.h>
#include <unistd.h>
#endif

#include <GLFW/glfw3.h>
#include "platform_linux.cpp"
#include "Bitmap.h"
#include "Bitmap.cpp"
#include "shader.hpp"
#include "shader.cpp"
#include "Drawable.h"
#include "Drawable.cpp"

#define FRAMES_PER_SECOND 30

#ifdef EMSCRIPTEN
static const char* noteVertSource = "
precision mediump float;n
attribute vec4 position;n
attribute vec2 verTexCoord;n
varying vec2 fragTexCoord;n
void main()n
{n
fragTexCoord = verTexCoord;n
gl_Position = position;n
}";

static const char* noteFragSource = "
precision mediump float;n
uniform sampler2D texture_;n
varying vec2 fragTexCoord;n
void main()n
{n
gl_FragColor = texture2D(texture_, fragTexCoord);n
}";

#else
static const char* noteVertSource = "
attribute vec4 position;n
attribute vec2 verTexCoord;n
varying vec2 fragTexCoord;n
void main()n
{n
fragTexCoord = verTexCoord;n
gl_Position = position;n
}";

static const char* noteFragSource = "
uniform sampler2D texture_;n
varying vec2 fragTexCoord;n
void main()n
{n
gl_FragColor = texture2D(texture_, fragTexCoord);n
}";

#endif


static const char* vertSource = "
#ifdef EMSCRIPTENn
precision mediump float;n
#endifn
attribute vec4 position;n
void main()n
{n
gl_Position = position;n
}";

static const char* fragSource = "
#ifdef EMSCRIPTENn
precision mediump float;n
#endifn
void main()n
{n
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);n
}";

void GLAPIENTRY
MessageCallback( GLenum source,
GLenum type,
GLuint id,
GLenum severity,
GLsizei length,
const GLchar* message,
const void* userParam )
{
(void) source;
(void) id;
(void) length;
(void) userParam;
const char *repr = type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "";
fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %sn",
repr, type, severity, message );
}

class GLFWContext
{
public:
GLFWContext(size_t width, size_t height, const char *name)
{
// Initialise GLFW
if( !glfwInit() )
{
throw std::runtime_error("Failed to initialize GLFW");
}

glfwWindowHint(GLFW_SAMPLES, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

// Open a window and create its OpenGL context
window = glfwCreateWindow(width, height, name, nullptr, nullptr);
if (!window)
{
glfwTerminate();
throw std::runtime_error("Failed to open GLFW window. "
"If you have an Intel GPU, they are not 3.3 compatible. "
"Try the 2.1 version of the tutorials.n");
}
glfwMakeContextCurrent(window);
}

~GLFWContext()
{
glfwDestroyWindow(window);
glfwTerminate();
}

bool isActive()
{
return glfwGetKey(window, GLFW_KEY_ESCAPE) != GLFW_PRESS &&
glfwWindowShouldClose(window) == 0;
}

void update()
{
glfwSwapBuffers(window);
glfwPollEvents();
}

private:
GLFWwindow* window;
};

class Game : public Drawable
{
public:
Game(GLFWContext& glfw) : glfw(glfw) {}

virtual void draw() const
{
glClear(GL_COLOR_BUFFER_BIT);
for (auto& object : objects)
object->draw();
}

inline void add(std::unique_ptr<Drawable> object)
{
objects.push_back(std::move(object));
}

virtual void update()
{
for (auto& object : objects) object->update();
glfw.update();
}
private:
GLFWContext& glfw;
std::vector<std::unique_ptr<Drawable>> objects;
};


void main_loop(void* arg)
{
#ifndef EMSCRIPTEN
const auto start = std::clock();
#endif
Game *game = (Game *)arg;
assert(game);

game->draw();
game->update();

unsigned int err = 0;
while ( (err = glGetError()) )
{
std::cerr << err << "n";
}

#ifndef EMSCRIPTEN
const auto seconds_elapsed = (std::clock() - start) /
static_cast<double>(CLOCKS_PER_SEC);
if (seconds_elapsed < 1.0 / FRAMES_PER_SECOND)
{
usleep(1000000 * (1.0 / FRAMES_PER_SECOND - seconds_elapsed));
}
#endif
}

int main( void )
{
GLFWContext context(1024, 768, "Music game");

// Initialize GLEW
#ifndef EMSCRIPTEN
glewExperimental = true; // Needed for core profile
if (glewInit() != GLEW_OK) {
fprintf(stderr, "Failed to initialize GLEWn");
return -1;
}

glEnable(GL_DEBUG_OUTPUT);
glDebugMessageCallback(MessageCallback, 0);
#endif

glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

glClearColor(0.9f, 0.9f, 0.9f, 1.0f);

// Create and compile our GLSL program from the shaders
const GLuint linesProgram = LoadShaders(vertSource, fragSource);
const GLuint noteProgram = LoadShaders(noteVertSource, noteFragSource);

Game game(context);
Note note(noteProgram);
note.setY(.75);
game.add(std::unique_ptr<Drawable>(
new Staff(linesProgram, note)));

#ifdef EMSCRIPTEN
emscripten_set_main_loop_arg(main_loop, &game, FRAMES_PER_SECOND, 1);
#else
do{
main_loop(&game);
} while (context.isActive());
#endif

return 0;
}









share|improve this question









$endgroup$

















    4












    $begingroup$


    This program draws a random music note on a staff, refreshing the note every second. It compiles with g++ and also emscripten, so it is runnable in both the browser and on desktop.



    This is my first attempt at graphics programming, so I'm not interested in feedback on how I'm using OpenGL. In particular, I'm vaguely considered that I'm using shaders poorly. Do I really write a new shader program for each sort of thing I have to draw?



    This code uses Bitmap.h, Bitmap.cpp, platform.hpp, and platform_linux.cpp by Thomas Dalling. These files assume that stb_image.h is located on the system include path. shader.cpp is modified from the shader code in this tutorial, but it's just standard shader-loading code. This project uses a unity build.



    Makefile



    all: a.out index.html

    EMS_OPTS = -s FULL_ES3=1
    -s WASM=1
    -s NO_EXIT_RUNTIME=1
    -s USE_GLFW=3
    -s USE_LIBPNG=1
    -DEMSCRIPTEN

    CXX_FLAGS = -Wall -Wextra -std=c++17
    LD_LFAGS = -lGL -lGLU -lglfw -lGLEW -lpng

    index.html: main.cpp Drawable.cpp Drawable.h
    emcc main.cpp -o index.html ${CXX_FLAGS} ${EMS_OPTS} --preload-file resources/whole-note.png

    a.out: main.cpp Drawable.cpp Drawable.h
    g++ -g main.cpp ${CXX_FLAGS} ${LD_LFAGS}


    Drawable.h



    #ifndef DRAWABLE_H_
    #define DRAWABLE_H_

    #include <chrono>
    #include <memory>
    #include <vector>

    class Drawable
    {
    public:
    virtual ~Drawable() {}
    virtual void draw() const = 0;
    virtual void update() {};
    Drawable(const Drawable& other) = delete;
    Drawable& operator=(const Drawable& other) = delete;
    Drawable() {}
    };

    class Note : public Drawable
    {
    public:
    Note(GLuint program);
    ~Note();
    virtual void draw() const;

    void setY(double value);
    private:
    GLuint vao, vbo, ibo, texture, program;
    };

    class Staff : public Drawable
    {
    public:
    Staff(GLuint program, Note& note);
    ~Staff();
    virtual void draw() const;
    virtual void update();
    private:
    GLuint vao, vbo, program;
    static const GLfloat points[30];
    Note& note;
    std::chrono::time_point<std::chrono::system_clock> start;
    std::vector<float> valid_positions;
    };

    #endif


    Drawable.cpp



    #include <algorithm>
    #include <random>
    #include "Drawable.h"

    namespace
    {
    GLfloat vertices = {
    // X, Y, Z U, V
    0.2, 0.0, 0.0, 1.0, 1.0,
    -0.2, 0.0, 0.0, 0.0, 1.0,
    0.2, 0.0, 0.0, 1.0, 0.0,
    -0.2, 0.0, 0.0, 0.0, 0.0,
    };
    }

    Note::Note(GLuint program) :
    program(program)
    {
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);

    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // X, Y, Z (dest) coordinates
    glVertexAttribPointer(0,
    3,
    GL_FLOAT,
    GL_FALSE,
    5 * sizeof(GL_FLOAT),
    NULL);
    glEnableVertexAttribArray(0);

    // U, V (src) coordinates
    glVertexAttribPointer(1,
    2,
    GL_FLOAT,
    GL_FALSE,
    5 * sizeof(GL_FLOAT),
    (const GLvoid *)(3 * sizeof(GL_FLOAT)));
    glEnableVertexAttribArray(1);

    // TODO: Pass this in?
    auto bmp = tdogl::Bitmap::bitmapFromFile(ResourcePath("whole-note.png"));
    bmp.flipVertically();

    // Index buffer object
    glGenBuffers(1, &ibo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
    static const GLuint indexData = {
    0, 1, 2,
    2, 1, 3
    };
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indexData), indexData,
    GL_STATIC_DRAW);
    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D,
    0,
    GL_RGBA,
    (GLsizei)bmp.width(),
    (GLsizei)bmp.height(),
    0,
    GL_RGBA,
    GL_UNSIGNED_BYTE, bmp.pixelBuffer());
    glBindTexture(GL_TEXTURE_2D, 0);
    }

    void Note::setY(double value)
    {
    static const float HALF_DIM = .2;
    vertices[1] = value + HALF_DIM;
    vertices[6] = value + HALF_DIM;
    vertices[11] = value - HALF_DIM;
    vertices[16] = value - HALF_DIM;

    glBindVertexArray(vao);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
    }

    Note::~Note()
    {
    glDeleteProgram(program);
    glDeleteBuffers(1, &vbo);
    glDeleteBuffers(1, &ibo);
    glDeleteVertexArrays(1, &vao);
    glDeleteTextures(1, &texture);
    }

    void Note::draw() const
    {
    glUseProgram(program);
    glBindVertexArray(vao);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture);

    GLint uniform = glGetUniformLocation(program, "texture_");
    glUniform1i(uniform, 0);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

    glBindVertexArray(0);
    }

    Staff::Staff(GLuint program, Note& note) :
    program(program),
    note(note),
    start(std::chrono::system_clock::now()),
    valid_positions{.5, .55, .6, .65, .7, .75, .8, .85}
    {
    note.setY(valid_positions[0]);
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);

    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);

    glVertexAttribPointer(
    0,
    3,
    GL_FLOAT,
    GL_FALSE,
    0,
    (void*)0
    );
    glEnableVertexAttribArray(0);
    glBindVertexArray(0);
    }

    Staff::~Staff()
    {
    glDeleteBuffers(1, &vbo);
    glDeleteVertexArrays(1, &vao);
    glDeleteProgram(program);
    }

    void Staff::draw() const
    {
    glUseProgram(program);
    glBindVertexArray(vao);
    glDrawArrays(GL_LINES, 0, 10);

    note.draw();
    }

    const GLfloat Staff::points[30] = {
    -1.0f, 0.9f, 0.0f,
    1.0f, 0.9f, 0.0f,

    -1.0f, 0.8f, 0.0f,
    1.0f, 0.8f, 0.0f,

    -1.0f, 0.7f, 0.0f,
    1.0f, 0.7f, 0.0f,

    -1.0f, 0.6f, 0.0f,
    1.0f, 0.6f, 0.0f,

    -1.0f, 0.5f, 0.0f,
    1.0f, 0.5f, 0.0f,
    };

    void Staff::update()
    {
    const auto current_time = std::chrono::system_clock::now();
    const auto duration = std::chrono::duration_cast<std::chrono::seconds>(current_time - start).count();
    if (duration >= 1)
    {
    start = std::chrono::system_clock::now();

    std::vector<float> new_position;
    std::sample(valid_positions.begin(), valid_positions.end(),
    std::back_inserter(new_position), 1,
    std::mt19937{std::random_device{}()});
    note.setY(new_position[0]);
    }
    }


    main.cpp



    #include <iostream>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <vector>
    #include <memory>

    #ifdef EMSCRIPTEN
    #include <GLES3/gl3.h>
    #include "emscripten.h"
    #else
    #include <GL/glew.h>
    #include <unistd.h>
    #endif

    #include <GLFW/glfw3.h>
    #include "platform_linux.cpp"
    #include "Bitmap.h"
    #include "Bitmap.cpp"
    #include "shader.hpp"
    #include "shader.cpp"
    #include "Drawable.h"
    #include "Drawable.cpp"

    #define FRAMES_PER_SECOND 30

    #ifdef EMSCRIPTEN
    static const char* noteVertSource = "
    precision mediump float;n
    attribute vec4 position;n
    attribute vec2 verTexCoord;n
    varying vec2 fragTexCoord;n
    void main()n
    {n
    fragTexCoord = verTexCoord;n
    gl_Position = position;n
    }";

    static const char* noteFragSource = "
    precision mediump float;n
    uniform sampler2D texture_;n
    varying vec2 fragTexCoord;n
    void main()n
    {n
    gl_FragColor = texture2D(texture_, fragTexCoord);n
    }";

    #else
    static const char* noteVertSource = "
    attribute vec4 position;n
    attribute vec2 verTexCoord;n
    varying vec2 fragTexCoord;n
    void main()n
    {n
    fragTexCoord = verTexCoord;n
    gl_Position = position;n
    }";

    static const char* noteFragSource = "
    uniform sampler2D texture_;n
    varying vec2 fragTexCoord;n
    void main()n
    {n
    gl_FragColor = texture2D(texture_, fragTexCoord);n
    }";

    #endif


    static const char* vertSource = "
    #ifdef EMSCRIPTENn
    precision mediump float;n
    #endifn
    attribute vec4 position;n
    void main()n
    {n
    gl_Position = position;n
    }";

    static const char* fragSource = "
    #ifdef EMSCRIPTENn
    precision mediump float;n
    #endifn
    void main()n
    {n
    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);n
    }";

    void GLAPIENTRY
    MessageCallback( GLenum source,
    GLenum type,
    GLuint id,
    GLenum severity,
    GLsizei length,
    const GLchar* message,
    const void* userParam )
    {
    (void) source;
    (void) id;
    (void) length;
    (void) userParam;
    const char *repr = type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "";
    fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %sn",
    repr, type, severity, message );
    }

    class GLFWContext
    {
    public:
    GLFWContext(size_t width, size_t height, const char *name)
    {
    // Initialise GLFW
    if( !glfwInit() )
    {
    throw std::runtime_error("Failed to initialize GLFW");
    }

    glfwWindowHint(GLFW_SAMPLES, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    // Open a window and create its OpenGL context
    window = glfwCreateWindow(width, height, name, nullptr, nullptr);
    if (!window)
    {
    glfwTerminate();
    throw std::runtime_error("Failed to open GLFW window. "
    "If you have an Intel GPU, they are not 3.3 compatible. "
    "Try the 2.1 version of the tutorials.n");
    }
    glfwMakeContextCurrent(window);
    }

    ~GLFWContext()
    {
    glfwDestroyWindow(window);
    glfwTerminate();
    }

    bool isActive()
    {
    return glfwGetKey(window, GLFW_KEY_ESCAPE) != GLFW_PRESS &&
    glfwWindowShouldClose(window) == 0;
    }

    void update()
    {
    glfwSwapBuffers(window);
    glfwPollEvents();
    }

    private:
    GLFWwindow* window;
    };

    class Game : public Drawable
    {
    public:
    Game(GLFWContext& glfw) : glfw(glfw) {}

    virtual void draw() const
    {
    glClear(GL_COLOR_BUFFER_BIT);
    for (auto& object : objects)
    object->draw();
    }

    inline void add(std::unique_ptr<Drawable> object)
    {
    objects.push_back(std::move(object));
    }

    virtual void update()
    {
    for (auto& object : objects) object->update();
    glfw.update();
    }
    private:
    GLFWContext& glfw;
    std::vector<std::unique_ptr<Drawable>> objects;
    };


    void main_loop(void* arg)
    {
    #ifndef EMSCRIPTEN
    const auto start = std::clock();
    #endif
    Game *game = (Game *)arg;
    assert(game);

    game->draw();
    game->update();

    unsigned int err = 0;
    while ( (err = glGetError()) )
    {
    std::cerr << err << "n";
    }

    #ifndef EMSCRIPTEN
    const auto seconds_elapsed = (std::clock() - start) /
    static_cast<double>(CLOCKS_PER_SEC);
    if (seconds_elapsed < 1.0 / FRAMES_PER_SECOND)
    {
    usleep(1000000 * (1.0 / FRAMES_PER_SECOND - seconds_elapsed));
    }
    #endif
    }

    int main( void )
    {
    GLFWContext context(1024, 768, "Music game");

    // Initialize GLEW
    #ifndef EMSCRIPTEN
    glewExperimental = true; // Needed for core profile
    if (glewInit() != GLEW_OK) {
    fprintf(stderr, "Failed to initialize GLEWn");
    return -1;
    }

    glEnable(GL_DEBUG_OUTPUT);
    glDebugMessageCallback(MessageCallback, 0);
    #endif

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glClearColor(0.9f, 0.9f, 0.9f, 1.0f);

    // Create and compile our GLSL program from the shaders
    const GLuint linesProgram = LoadShaders(vertSource, fragSource);
    const GLuint noteProgram = LoadShaders(noteVertSource, noteFragSource);

    Game game(context);
    Note note(noteProgram);
    note.setY(.75);
    game.add(std::unique_ptr<Drawable>(
    new Staff(linesProgram, note)));

    #ifdef EMSCRIPTEN
    emscripten_set_main_loop_arg(main_loop, &game, FRAMES_PER_SECOND, 1);
    #else
    do{
    main_loop(&game);
    } while (context.isActive());
    #endif

    return 0;
    }









    share|improve this question









    $endgroup$















      4












      4








      4





      $begingroup$


      This program draws a random music note on a staff, refreshing the note every second. It compiles with g++ and also emscripten, so it is runnable in both the browser and on desktop.



      This is my first attempt at graphics programming, so I'm not interested in feedback on how I'm using OpenGL. In particular, I'm vaguely considered that I'm using shaders poorly. Do I really write a new shader program for each sort of thing I have to draw?



      This code uses Bitmap.h, Bitmap.cpp, platform.hpp, and platform_linux.cpp by Thomas Dalling. These files assume that stb_image.h is located on the system include path. shader.cpp is modified from the shader code in this tutorial, but it's just standard shader-loading code. This project uses a unity build.



      Makefile



      all: a.out index.html

      EMS_OPTS = -s FULL_ES3=1
      -s WASM=1
      -s NO_EXIT_RUNTIME=1
      -s USE_GLFW=3
      -s USE_LIBPNG=1
      -DEMSCRIPTEN

      CXX_FLAGS = -Wall -Wextra -std=c++17
      LD_LFAGS = -lGL -lGLU -lglfw -lGLEW -lpng

      index.html: main.cpp Drawable.cpp Drawable.h
      emcc main.cpp -o index.html ${CXX_FLAGS} ${EMS_OPTS} --preload-file resources/whole-note.png

      a.out: main.cpp Drawable.cpp Drawable.h
      g++ -g main.cpp ${CXX_FLAGS} ${LD_LFAGS}


      Drawable.h



      #ifndef DRAWABLE_H_
      #define DRAWABLE_H_

      #include <chrono>
      #include <memory>
      #include <vector>

      class Drawable
      {
      public:
      virtual ~Drawable() {}
      virtual void draw() const = 0;
      virtual void update() {};
      Drawable(const Drawable& other) = delete;
      Drawable& operator=(const Drawable& other) = delete;
      Drawable() {}
      };

      class Note : public Drawable
      {
      public:
      Note(GLuint program);
      ~Note();
      virtual void draw() const;

      void setY(double value);
      private:
      GLuint vao, vbo, ibo, texture, program;
      };

      class Staff : public Drawable
      {
      public:
      Staff(GLuint program, Note& note);
      ~Staff();
      virtual void draw() const;
      virtual void update();
      private:
      GLuint vao, vbo, program;
      static const GLfloat points[30];
      Note& note;
      std::chrono::time_point<std::chrono::system_clock> start;
      std::vector<float> valid_positions;
      };

      #endif


      Drawable.cpp



      #include <algorithm>
      #include <random>
      #include "Drawable.h"

      namespace
      {
      GLfloat vertices = {
      // X, Y, Z U, V
      0.2, 0.0, 0.0, 1.0, 1.0,
      -0.2, 0.0, 0.0, 0.0, 1.0,
      0.2, 0.0, 0.0, 1.0, 0.0,
      -0.2, 0.0, 0.0, 0.0, 0.0,
      };
      }

      Note::Note(GLuint program) :
      program(program)
      {
      glGenVertexArrays(1, &vao);
      glBindVertexArray(vao);

      glGenBuffers(1, &vbo);
      glBindBuffer(GL_ARRAY_BUFFER, vbo);
      glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

      // X, Y, Z (dest) coordinates
      glVertexAttribPointer(0,
      3,
      GL_FLOAT,
      GL_FALSE,
      5 * sizeof(GL_FLOAT),
      NULL);
      glEnableVertexAttribArray(0);

      // U, V (src) coordinates
      glVertexAttribPointer(1,
      2,
      GL_FLOAT,
      GL_FALSE,
      5 * sizeof(GL_FLOAT),
      (const GLvoid *)(3 * sizeof(GL_FLOAT)));
      glEnableVertexAttribArray(1);

      // TODO: Pass this in?
      auto bmp = tdogl::Bitmap::bitmapFromFile(ResourcePath("whole-note.png"));
      bmp.flipVertically();

      // Index buffer object
      glGenBuffers(1, &ibo);
      glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
      static const GLuint indexData = {
      0, 1, 2,
      2, 1, 3
      };
      glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indexData), indexData,
      GL_STATIC_DRAW);
      glBindVertexArray(0);
      glBindBuffer(GL_ARRAY_BUFFER, 0);
      glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

      glGenTextures(1, &texture);
      glBindTexture(GL_TEXTURE_2D, texture);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
      glTexImage2D(GL_TEXTURE_2D,
      0,
      GL_RGBA,
      (GLsizei)bmp.width(),
      (GLsizei)bmp.height(),
      0,
      GL_RGBA,
      GL_UNSIGNED_BYTE, bmp.pixelBuffer());
      glBindTexture(GL_TEXTURE_2D, 0);
      }

      void Note::setY(double value)
      {
      static const float HALF_DIM = .2;
      vertices[1] = value + HALF_DIM;
      vertices[6] = value + HALF_DIM;
      vertices[11] = value - HALF_DIM;
      vertices[16] = value - HALF_DIM;

      glBindVertexArray(vao);
      glBindBuffer(GL_ARRAY_BUFFER, vbo);
      glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
      glBindBuffer(GL_ARRAY_BUFFER, 0);
      glBindVertexArray(0);
      }

      Note::~Note()
      {
      glDeleteProgram(program);
      glDeleteBuffers(1, &vbo);
      glDeleteBuffers(1, &ibo);
      glDeleteVertexArrays(1, &vao);
      glDeleteTextures(1, &texture);
      }

      void Note::draw() const
      {
      glUseProgram(program);
      glBindVertexArray(vao);
      glActiveTexture(GL_TEXTURE0);
      glBindTexture(GL_TEXTURE_2D, texture);

      GLint uniform = glGetUniformLocation(program, "texture_");
      glUniform1i(uniform, 0);
      glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

      glBindVertexArray(0);
      }

      Staff::Staff(GLuint program, Note& note) :
      program(program),
      note(note),
      start(std::chrono::system_clock::now()),
      valid_positions{.5, .55, .6, .65, .7, .75, .8, .85}
      {
      note.setY(valid_positions[0]);
      glGenVertexArrays(1, &vao);
      glBindVertexArray(vao);

      glGenBuffers(1, &vbo);
      glBindBuffer(GL_ARRAY_BUFFER, vbo);
      glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);

      glVertexAttribPointer(
      0,
      3,
      GL_FLOAT,
      GL_FALSE,
      0,
      (void*)0
      );
      glEnableVertexAttribArray(0);
      glBindVertexArray(0);
      }

      Staff::~Staff()
      {
      glDeleteBuffers(1, &vbo);
      glDeleteVertexArrays(1, &vao);
      glDeleteProgram(program);
      }

      void Staff::draw() const
      {
      glUseProgram(program);
      glBindVertexArray(vao);
      glDrawArrays(GL_LINES, 0, 10);

      note.draw();
      }

      const GLfloat Staff::points[30] = {
      -1.0f, 0.9f, 0.0f,
      1.0f, 0.9f, 0.0f,

      -1.0f, 0.8f, 0.0f,
      1.0f, 0.8f, 0.0f,

      -1.0f, 0.7f, 0.0f,
      1.0f, 0.7f, 0.0f,

      -1.0f, 0.6f, 0.0f,
      1.0f, 0.6f, 0.0f,

      -1.0f, 0.5f, 0.0f,
      1.0f, 0.5f, 0.0f,
      };

      void Staff::update()
      {
      const auto current_time = std::chrono::system_clock::now();
      const auto duration = std::chrono::duration_cast<std::chrono::seconds>(current_time - start).count();
      if (duration >= 1)
      {
      start = std::chrono::system_clock::now();

      std::vector<float> new_position;
      std::sample(valid_positions.begin(), valid_positions.end(),
      std::back_inserter(new_position), 1,
      std::mt19937{std::random_device{}()});
      note.setY(new_position[0]);
      }
      }


      main.cpp



      #include <iostream>
      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      #include <vector>
      #include <memory>

      #ifdef EMSCRIPTEN
      #include <GLES3/gl3.h>
      #include "emscripten.h"
      #else
      #include <GL/glew.h>
      #include <unistd.h>
      #endif

      #include <GLFW/glfw3.h>
      #include "platform_linux.cpp"
      #include "Bitmap.h"
      #include "Bitmap.cpp"
      #include "shader.hpp"
      #include "shader.cpp"
      #include "Drawable.h"
      #include "Drawable.cpp"

      #define FRAMES_PER_SECOND 30

      #ifdef EMSCRIPTEN
      static const char* noteVertSource = "
      precision mediump float;n
      attribute vec4 position;n
      attribute vec2 verTexCoord;n
      varying vec2 fragTexCoord;n
      void main()n
      {n
      fragTexCoord = verTexCoord;n
      gl_Position = position;n
      }";

      static const char* noteFragSource = "
      precision mediump float;n
      uniform sampler2D texture_;n
      varying vec2 fragTexCoord;n
      void main()n
      {n
      gl_FragColor = texture2D(texture_, fragTexCoord);n
      }";

      #else
      static const char* noteVertSource = "
      attribute vec4 position;n
      attribute vec2 verTexCoord;n
      varying vec2 fragTexCoord;n
      void main()n
      {n
      fragTexCoord = verTexCoord;n
      gl_Position = position;n
      }";

      static const char* noteFragSource = "
      uniform sampler2D texture_;n
      varying vec2 fragTexCoord;n
      void main()n
      {n
      gl_FragColor = texture2D(texture_, fragTexCoord);n
      }";

      #endif


      static const char* vertSource = "
      #ifdef EMSCRIPTENn
      precision mediump float;n
      #endifn
      attribute vec4 position;n
      void main()n
      {n
      gl_Position = position;n
      }";

      static const char* fragSource = "
      #ifdef EMSCRIPTENn
      precision mediump float;n
      #endifn
      void main()n
      {n
      gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);n
      }";

      void GLAPIENTRY
      MessageCallback( GLenum source,
      GLenum type,
      GLuint id,
      GLenum severity,
      GLsizei length,
      const GLchar* message,
      const void* userParam )
      {
      (void) source;
      (void) id;
      (void) length;
      (void) userParam;
      const char *repr = type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "";
      fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %sn",
      repr, type, severity, message );
      }

      class GLFWContext
      {
      public:
      GLFWContext(size_t width, size_t height, const char *name)
      {
      // Initialise GLFW
      if( !glfwInit() )
      {
      throw std::runtime_error("Failed to initialize GLFW");
      }

      glfwWindowHint(GLFW_SAMPLES, 4);
      glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
      glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
      glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed
      glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

      // Open a window and create its OpenGL context
      window = glfwCreateWindow(width, height, name, nullptr, nullptr);
      if (!window)
      {
      glfwTerminate();
      throw std::runtime_error("Failed to open GLFW window. "
      "If you have an Intel GPU, they are not 3.3 compatible. "
      "Try the 2.1 version of the tutorials.n");
      }
      glfwMakeContextCurrent(window);
      }

      ~GLFWContext()
      {
      glfwDestroyWindow(window);
      glfwTerminate();
      }

      bool isActive()
      {
      return glfwGetKey(window, GLFW_KEY_ESCAPE) != GLFW_PRESS &&
      glfwWindowShouldClose(window) == 0;
      }

      void update()
      {
      glfwSwapBuffers(window);
      glfwPollEvents();
      }

      private:
      GLFWwindow* window;
      };

      class Game : public Drawable
      {
      public:
      Game(GLFWContext& glfw) : glfw(glfw) {}

      virtual void draw() const
      {
      glClear(GL_COLOR_BUFFER_BIT);
      for (auto& object : objects)
      object->draw();
      }

      inline void add(std::unique_ptr<Drawable> object)
      {
      objects.push_back(std::move(object));
      }

      virtual void update()
      {
      for (auto& object : objects) object->update();
      glfw.update();
      }
      private:
      GLFWContext& glfw;
      std::vector<std::unique_ptr<Drawable>> objects;
      };


      void main_loop(void* arg)
      {
      #ifndef EMSCRIPTEN
      const auto start = std::clock();
      #endif
      Game *game = (Game *)arg;
      assert(game);

      game->draw();
      game->update();

      unsigned int err = 0;
      while ( (err = glGetError()) )
      {
      std::cerr << err << "n";
      }

      #ifndef EMSCRIPTEN
      const auto seconds_elapsed = (std::clock() - start) /
      static_cast<double>(CLOCKS_PER_SEC);
      if (seconds_elapsed < 1.0 / FRAMES_PER_SECOND)
      {
      usleep(1000000 * (1.0 / FRAMES_PER_SECOND - seconds_elapsed));
      }
      #endif
      }

      int main( void )
      {
      GLFWContext context(1024, 768, "Music game");

      // Initialize GLEW
      #ifndef EMSCRIPTEN
      glewExperimental = true; // Needed for core profile
      if (glewInit() != GLEW_OK) {
      fprintf(stderr, "Failed to initialize GLEWn");
      return -1;
      }

      glEnable(GL_DEBUG_OUTPUT);
      glDebugMessageCallback(MessageCallback, 0);
      #endif

      glEnable(GL_BLEND);
      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

      glClearColor(0.9f, 0.9f, 0.9f, 1.0f);

      // Create and compile our GLSL program from the shaders
      const GLuint linesProgram = LoadShaders(vertSource, fragSource);
      const GLuint noteProgram = LoadShaders(noteVertSource, noteFragSource);

      Game game(context);
      Note note(noteProgram);
      note.setY(.75);
      game.add(std::unique_ptr<Drawable>(
      new Staff(linesProgram, note)));

      #ifdef EMSCRIPTEN
      emscripten_set_main_loop_arg(main_loop, &game, FRAMES_PER_SECOND, 1);
      #else
      do{
      main_loop(&game);
      } while (context.isActive());
      #endif

      return 0;
      }









      share|improve this question









      $endgroup$




      This program draws a random music note on a staff, refreshing the note every second. It compiles with g++ and also emscripten, so it is runnable in both the browser and on desktop.



      This is my first attempt at graphics programming, so I'm not interested in feedback on how I'm using OpenGL. In particular, I'm vaguely considered that I'm using shaders poorly. Do I really write a new shader program for each sort of thing I have to draw?



      This code uses Bitmap.h, Bitmap.cpp, platform.hpp, and platform_linux.cpp by Thomas Dalling. These files assume that stb_image.h is located on the system include path. shader.cpp is modified from the shader code in this tutorial, but it's just standard shader-loading code. This project uses a unity build.



      Makefile



      all: a.out index.html

      EMS_OPTS = -s FULL_ES3=1
      -s WASM=1
      -s NO_EXIT_RUNTIME=1
      -s USE_GLFW=3
      -s USE_LIBPNG=1
      -DEMSCRIPTEN

      CXX_FLAGS = -Wall -Wextra -std=c++17
      LD_LFAGS = -lGL -lGLU -lglfw -lGLEW -lpng

      index.html: main.cpp Drawable.cpp Drawable.h
      emcc main.cpp -o index.html ${CXX_FLAGS} ${EMS_OPTS} --preload-file resources/whole-note.png

      a.out: main.cpp Drawable.cpp Drawable.h
      g++ -g main.cpp ${CXX_FLAGS} ${LD_LFAGS}


      Drawable.h



      #ifndef DRAWABLE_H_
      #define DRAWABLE_H_

      #include <chrono>
      #include <memory>
      #include <vector>

      class Drawable
      {
      public:
      virtual ~Drawable() {}
      virtual void draw() const = 0;
      virtual void update() {};
      Drawable(const Drawable& other) = delete;
      Drawable& operator=(const Drawable& other) = delete;
      Drawable() {}
      };

      class Note : public Drawable
      {
      public:
      Note(GLuint program);
      ~Note();
      virtual void draw() const;

      void setY(double value);
      private:
      GLuint vao, vbo, ibo, texture, program;
      };

      class Staff : public Drawable
      {
      public:
      Staff(GLuint program, Note& note);
      ~Staff();
      virtual void draw() const;
      virtual void update();
      private:
      GLuint vao, vbo, program;
      static const GLfloat points[30];
      Note& note;
      std::chrono::time_point<std::chrono::system_clock> start;
      std::vector<float> valid_positions;
      };

      #endif


      Drawable.cpp



      #include <algorithm>
      #include <random>
      #include "Drawable.h"

      namespace
      {
      GLfloat vertices = {
      // X, Y, Z U, V
      0.2, 0.0, 0.0, 1.0, 1.0,
      -0.2, 0.0, 0.0, 0.0, 1.0,
      0.2, 0.0, 0.0, 1.0, 0.0,
      -0.2, 0.0, 0.0, 0.0, 0.0,
      };
      }

      Note::Note(GLuint program) :
      program(program)
      {
      glGenVertexArrays(1, &vao);
      glBindVertexArray(vao);

      glGenBuffers(1, &vbo);
      glBindBuffer(GL_ARRAY_BUFFER, vbo);
      glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

      // X, Y, Z (dest) coordinates
      glVertexAttribPointer(0,
      3,
      GL_FLOAT,
      GL_FALSE,
      5 * sizeof(GL_FLOAT),
      NULL);
      glEnableVertexAttribArray(0);

      // U, V (src) coordinates
      glVertexAttribPointer(1,
      2,
      GL_FLOAT,
      GL_FALSE,
      5 * sizeof(GL_FLOAT),
      (const GLvoid *)(3 * sizeof(GL_FLOAT)));
      glEnableVertexAttribArray(1);

      // TODO: Pass this in?
      auto bmp = tdogl::Bitmap::bitmapFromFile(ResourcePath("whole-note.png"));
      bmp.flipVertically();

      // Index buffer object
      glGenBuffers(1, &ibo);
      glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
      static const GLuint indexData = {
      0, 1, 2,
      2, 1, 3
      };
      glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indexData), indexData,
      GL_STATIC_DRAW);
      glBindVertexArray(0);
      glBindBuffer(GL_ARRAY_BUFFER, 0);
      glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

      glGenTextures(1, &texture);
      glBindTexture(GL_TEXTURE_2D, texture);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
      glTexImage2D(GL_TEXTURE_2D,
      0,
      GL_RGBA,
      (GLsizei)bmp.width(),
      (GLsizei)bmp.height(),
      0,
      GL_RGBA,
      GL_UNSIGNED_BYTE, bmp.pixelBuffer());
      glBindTexture(GL_TEXTURE_2D, 0);
      }

      void Note::setY(double value)
      {
      static const float HALF_DIM = .2;
      vertices[1] = value + HALF_DIM;
      vertices[6] = value + HALF_DIM;
      vertices[11] = value - HALF_DIM;
      vertices[16] = value - HALF_DIM;

      glBindVertexArray(vao);
      glBindBuffer(GL_ARRAY_BUFFER, vbo);
      glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
      glBindBuffer(GL_ARRAY_BUFFER, 0);
      glBindVertexArray(0);
      }

      Note::~Note()
      {
      glDeleteProgram(program);
      glDeleteBuffers(1, &vbo);
      glDeleteBuffers(1, &ibo);
      glDeleteVertexArrays(1, &vao);
      glDeleteTextures(1, &texture);
      }

      void Note::draw() const
      {
      glUseProgram(program);
      glBindVertexArray(vao);
      glActiveTexture(GL_TEXTURE0);
      glBindTexture(GL_TEXTURE_2D, texture);

      GLint uniform = glGetUniformLocation(program, "texture_");
      glUniform1i(uniform, 0);
      glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

      glBindVertexArray(0);
      }

      Staff::Staff(GLuint program, Note& note) :
      program(program),
      note(note),
      start(std::chrono::system_clock::now()),
      valid_positions{.5, .55, .6, .65, .7, .75, .8, .85}
      {
      note.setY(valid_positions[0]);
      glGenVertexArrays(1, &vao);
      glBindVertexArray(vao);

      glGenBuffers(1, &vbo);
      glBindBuffer(GL_ARRAY_BUFFER, vbo);
      glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);

      glVertexAttribPointer(
      0,
      3,
      GL_FLOAT,
      GL_FALSE,
      0,
      (void*)0
      );
      glEnableVertexAttribArray(0);
      glBindVertexArray(0);
      }

      Staff::~Staff()
      {
      glDeleteBuffers(1, &vbo);
      glDeleteVertexArrays(1, &vao);
      glDeleteProgram(program);
      }

      void Staff::draw() const
      {
      glUseProgram(program);
      glBindVertexArray(vao);
      glDrawArrays(GL_LINES, 0, 10);

      note.draw();
      }

      const GLfloat Staff::points[30] = {
      -1.0f, 0.9f, 0.0f,
      1.0f, 0.9f, 0.0f,

      -1.0f, 0.8f, 0.0f,
      1.0f, 0.8f, 0.0f,

      -1.0f, 0.7f, 0.0f,
      1.0f, 0.7f, 0.0f,

      -1.0f, 0.6f, 0.0f,
      1.0f, 0.6f, 0.0f,

      -1.0f, 0.5f, 0.0f,
      1.0f, 0.5f, 0.0f,
      };

      void Staff::update()
      {
      const auto current_time = std::chrono::system_clock::now();
      const auto duration = std::chrono::duration_cast<std::chrono::seconds>(current_time - start).count();
      if (duration >= 1)
      {
      start = std::chrono::system_clock::now();

      std::vector<float> new_position;
      std::sample(valid_positions.begin(), valid_positions.end(),
      std::back_inserter(new_position), 1,
      std::mt19937{std::random_device{}()});
      note.setY(new_position[0]);
      }
      }


      main.cpp



      #include <iostream>
      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      #include <vector>
      #include <memory>

      #ifdef EMSCRIPTEN
      #include <GLES3/gl3.h>
      #include "emscripten.h"
      #else
      #include <GL/glew.h>
      #include <unistd.h>
      #endif

      #include <GLFW/glfw3.h>
      #include "platform_linux.cpp"
      #include "Bitmap.h"
      #include "Bitmap.cpp"
      #include "shader.hpp"
      #include "shader.cpp"
      #include "Drawable.h"
      #include "Drawable.cpp"

      #define FRAMES_PER_SECOND 30

      #ifdef EMSCRIPTEN
      static const char* noteVertSource = "
      precision mediump float;n
      attribute vec4 position;n
      attribute vec2 verTexCoord;n
      varying vec2 fragTexCoord;n
      void main()n
      {n
      fragTexCoord = verTexCoord;n
      gl_Position = position;n
      }";

      static const char* noteFragSource = "
      precision mediump float;n
      uniform sampler2D texture_;n
      varying vec2 fragTexCoord;n
      void main()n
      {n
      gl_FragColor = texture2D(texture_, fragTexCoord);n
      }";

      #else
      static const char* noteVertSource = "
      attribute vec4 position;n
      attribute vec2 verTexCoord;n
      varying vec2 fragTexCoord;n
      void main()n
      {n
      fragTexCoord = verTexCoord;n
      gl_Position = position;n
      }";

      static const char* noteFragSource = "
      uniform sampler2D texture_;n
      varying vec2 fragTexCoord;n
      void main()n
      {n
      gl_FragColor = texture2D(texture_, fragTexCoord);n
      }";

      #endif


      static const char* vertSource = "
      #ifdef EMSCRIPTENn
      precision mediump float;n
      #endifn
      attribute vec4 position;n
      void main()n
      {n
      gl_Position = position;n
      }";

      static const char* fragSource = "
      #ifdef EMSCRIPTENn
      precision mediump float;n
      #endifn
      void main()n
      {n
      gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);n
      }";

      void GLAPIENTRY
      MessageCallback( GLenum source,
      GLenum type,
      GLuint id,
      GLenum severity,
      GLsizei length,
      const GLchar* message,
      const void* userParam )
      {
      (void) source;
      (void) id;
      (void) length;
      (void) userParam;
      const char *repr = type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "";
      fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %sn",
      repr, type, severity, message );
      }

      class GLFWContext
      {
      public:
      GLFWContext(size_t width, size_t height, const char *name)
      {
      // Initialise GLFW
      if( !glfwInit() )
      {
      throw std::runtime_error("Failed to initialize GLFW");
      }

      glfwWindowHint(GLFW_SAMPLES, 4);
      glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
      glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
      glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed
      glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

      // Open a window and create its OpenGL context
      window = glfwCreateWindow(width, height, name, nullptr, nullptr);
      if (!window)
      {
      glfwTerminate();
      throw std::runtime_error("Failed to open GLFW window. "
      "If you have an Intel GPU, they are not 3.3 compatible. "
      "Try the 2.1 version of the tutorials.n");
      }
      glfwMakeContextCurrent(window);
      }

      ~GLFWContext()
      {
      glfwDestroyWindow(window);
      glfwTerminate();
      }

      bool isActive()
      {
      return glfwGetKey(window, GLFW_KEY_ESCAPE) != GLFW_PRESS &&
      glfwWindowShouldClose(window) == 0;
      }

      void update()
      {
      glfwSwapBuffers(window);
      glfwPollEvents();
      }

      private:
      GLFWwindow* window;
      };

      class Game : public Drawable
      {
      public:
      Game(GLFWContext& glfw) : glfw(glfw) {}

      virtual void draw() const
      {
      glClear(GL_COLOR_BUFFER_BIT);
      for (auto& object : objects)
      object->draw();
      }

      inline void add(std::unique_ptr<Drawable> object)
      {
      objects.push_back(std::move(object));
      }

      virtual void update()
      {
      for (auto& object : objects) object->update();
      glfw.update();
      }
      private:
      GLFWContext& glfw;
      std::vector<std::unique_ptr<Drawable>> objects;
      };


      void main_loop(void* arg)
      {
      #ifndef EMSCRIPTEN
      const auto start = std::clock();
      #endif
      Game *game = (Game *)arg;
      assert(game);

      game->draw();
      game->update();

      unsigned int err = 0;
      while ( (err = glGetError()) )
      {
      std::cerr << err << "n";
      }

      #ifndef EMSCRIPTEN
      const auto seconds_elapsed = (std::clock() - start) /
      static_cast<double>(CLOCKS_PER_SEC);
      if (seconds_elapsed < 1.0 / FRAMES_PER_SECOND)
      {
      usleep(1000000 * (1.0 / FRAMES_PER_SECOND - seconds_elapsed));
      }
      #endif
      }

      int main( void )
      {
      GLFWContext context(1024, 768, "Music game");

      // Initialize GLEW
      #ifndef EMSCRIPTEN
      glewExperimental = true; // Needed for core profile
      if (glewInit() != GLEW_OK) {
      fprintf(stderr, "Failed to initialize GLEWn");
      return -1;
      }

      glEnable(GL_DEBUG_OUTPUT);
      glDebugMessageCallback(MessageCallback, 0);
      #endif

      glEnable(GL_BLEND);
      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

      glClearColor(0.9f, 0.9f, 0.9f, 1.0f);

      // Create and compile our GLSL program from the shaders
      const GLuint linesProgram = LoadShaders(vertSource, fragSource);
      const GLuint noteProgram = LoadShaders(noteVertSource, noteFragSource);

      Game game(context);
      Note note(noteProgram);
      note.setY(.75);
      game.add(std::unique_ptr<Drawable>(
      new Staff(linesProgram, note)));

      #ifdef EMSCRIPTEN
      emscripten_set_main_loop_arg(main_loop, &game, FRAMES_PER_SECOND, 1);
      #else
      do{
      main_loop(&game);
      } while (context.isActive());
      #endif

      return 0;
      }






      c++ opengl






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked 7 hours ago









      User319User319

      784415




      784415






















          0






          active

          oldest

          votes











          Your Answer





          StackExchange.ifUsing("editor", function () {
          return StackExchange.using("mathjaxEditing", function () {
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          });
          });
          }, "mathjax-editing");

          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "196"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f216083%2fnote-drawing-program%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          0






          active

          oldest

          votes








          0






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes
















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Code Review Stack Exchange!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          Use MathJax to format equations. MathJax reference.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f216083%2fnote-drawing-program%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          How to make a Squid Proxy server?

          Is this a new Fibonacci Identity?

          19世紀