#include <SFML/Graphics.hpp>
#include <GL/glew.h>
#include <iostream>
#include <cassert>
#include <stdexcept>
#include <cmath>
#define GLM_SWIZZLE
#include <glm/glm.hpp>
#include <glm/gtx/transform2.hpp>
#include <glm/gtc/type_ptr.hpp>
template<std::size_t ...>
struct add : std::integral_constant< std::size_t, 0 > {};
template<std::size_t X, std::size_t ... Xs>
struct add<X, Xs...> : std::integral_constant< std::size_t, X + add<Xs...>::value > {};
template <typename... Ts>
constexpr std::size_t size_of() { return add< sizeof(Ts)... >::value; }
struct Vertex
{
glm::vec3 position;
glm::vec4 color;
glm::vec2 texCoord;
Vertex(const glm::vec3& pos = {}, const glm::vec4& color = {}, const glm::vec2& texCoord = {})
: position(pos)
, color(color)
, texCoord(texCoord)
{}
friend std::ostream& operator << (std::ostream& out, const Vertex& v)
{
out << '(' << v.position.x << ',' << v.position.y << ',' << v.position.z << ')';
return out;
}
};
#define glCheck(expr) do { expr; getError(__FILE__, __LINE__); } while (false)
void getError(const char* file, int line)
{
GLenum error(glGetError());
if (error != GL_NO_ERROR)
{
std::string errorString;
switch (error)
{
case GL_INVALID_OPERATION: errorString = "INVALID_OPERATION"; break;
case GL_INVALID_ENUM: errorString = "INVALID_ENUM"; break;
case GL_INVALID_VALUE: errorString = "INVALID_VALUE"; break;
case GL_OUT_OF_MEMORY: errorString = "OUT_OF_MEMORY"; break;
case GL_INVALID_FRAMEBUFFER_OPERATION: errorString = "INVALID_FRAMEBUFFER_OPERATION"; break;
}
throw std::runtime_error(std::string("GL_" + errorString + " - ").append(file) + ":" + std::to_string(line));
error = glGetError();
}
}
// shader sources
#define GLSL(src) "#version 440 core\n" #src
const GLchar* vert = GLSL
(
layout(location = 0) in vec3 position;
layout(location = 1) in vec4 color;
layout(location = 2) in vec2 texCoord;
uniform mat4 MVP;
out vec4 color0;
out vec2 texCoord0;
void main()
{
gl_Position = MVP * vec4(position, 1.0);
texCoord0 = texCoord;
color0 = color;
}
);
const GLchar* frag = GLSL
(
in vec4 color0;
in vec2 texCoord0;
uniform sampler2D diffuse;
out vec4 color;
void main()
{
color = vec4(color0.rgb, texture(diffuse, texCoord0).a);
}
);
void checkStatus(GLuint obj)
{
GLint status = GL_FALSE;
if (glIsShader(obj)) glCheck(glGetShaderiv(obj, GL_COMPILE_STATUS, &status));
if (glIsProgram(obj)) glCheck(glGetProgramiv(obj, GL_LINK_STATUS, &status));
if (status == GL_TRUE) return;
glCheck(glDeleteShader(obj));
obj = 0;
}
void attachShader(GLuint program, GLenum type, const GLchar* src)
{
GLuint shader;
glCheck(shader = glCreateShader(type));
glCheck(glShaderSource(shader, 1, &src, NULL));
glCheck(glCompileShader(shader));
checkStatus(shader);
glCheck(glAttachShader(program, shader));
glCheck(glDeleteShader(shader));
}
GLuint loadShader(const GLchar* vert, const GLchar* frag, const GLchar* geom = nullptr)
{
GLuint progam;
glCheck(progam = glCreateProgram());
if (vert) attachShader(progam, GL_VERTEX_SHADER, vert);
if (geom) attachShader(progam, GL_GEOMETRY_SHADER, geom);
if (frag) attachShader(progam, GL_FRAGMENT_SHADER, frag);
glCheck(glLinkProgram(progam));
checkStatus(progam);
return progam;
}
void pollEvent(sf::Window& window)
{
static bool toggle = false;
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
if (event.type == sf::Event::KeyPressed) {
if (event.key.code == sf::Keyboard::Escape) {
window.close();
}
// for debug
else if (event.key.code == sf::Keyboard::D) {
toggle = !toggle;
if (toggle) glCheck(glPolygonMode(GL_FRONT_AND_BACK, GL_LINE));
else glCheck(glPolygonMode(GL_FRONT_AND_BACK, GL_FILL));
}
}
}
}
enum VAOs
{
Triangles,
NumVAOs
};
enum Buffers
{
ArrayBuffer,
IndexBuffer,
NumBuffers
};
void application()
{
sf::ContextSettings settings;
settings.depthBits = 24;
settings.stencilBits = 8;
settings.antialiasingLevel = 4;
settings.majorVersion = 4;
settings.minorVersion = 4;
sf::Window window({ 800, 600 }, "opengl template", sf::Style::Close, settings);
glm::mat4 orthoProjection = glm::ortho(0.f, static_cast<float>(window.getSize().x), static_cast<float>(window.getSize().y), 0.f);
if (glewInit() != GLEW_OK) {
throw std::runtime_error("Failed to initialize GLEW\n");
}
sf::Font font;
if (!font.loadFromFile("arial.ttf")) {
throw std::runtime_error("can't load font\n");
}
unsigned characterSize = 48;
std::string string = "h";
const auto& glyph = font.getGlyph(string[0], characterSize, false);
float x = 0.f;
float y = static_cast<float>(characterSize);
float left = glyph.bounds.left;
float top = glyph.bounds.top;
float right = left + glyph.bounds.width;
float bottom = top + glyph.bounds.height;
float u1 = static_cast<float>(glyph.textureRect.left);
float v1 = static_cast<float>(glyph.textureRect.top);
float u2 = u1 + glyph.textureRect.width;
float v2 = v1 + glyph.textureRect.height;
std::vector<Vertex> vertices = {
{ glm::vec3(x + left, y + top, 0), glm::vec4(1), glm::vec2(u1, v1) },
{ glm::vec3(x + left, y + bottom, 0), glm::vec4(1), glm::vec2(u1, v2) },
{ glm::vec3(x + right, y + bottom, 0), glm::vec4(1), glm::vec2(u2, v2) },
{ glm::vec3(x + right, y + top, 0), glm::vec4(1), glm::vec2(u2, v1) },
};
std::vector<GLuint> indices = { 0, 1, 3, 3, 1, 2 };
window.setActive();
GLuint VAOs[NumVAOs];
GLuint Buffers[NumBuffers];
glCheck(glGenVertexArrays(NumVAOs, VAOs));
glCheck(glGenBuffers(NumBuffers, Buffers));
glCheck(glBindVertexArray(VAOs[Triangles]));
glCheck(glBindBuffer(GL_ARRAY_BUFFER, Buffers[ArrayBuffer]));
glCheck(glBufferData(GL_ARRAY_BUFFER, vertices.size() * size_of<Vertex>(), vertices.data(), GL_STATIC_DRAW));
glCheck(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Buffers[IndexBuffer]));
glCheck(glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * size_of<GLuint>(), indices.data(), GL_STATIC_DRAW));
GLuint program = loadShader(vert, frag);
glCheck(glUseProgram(program));
GLint position;
glCheck(position = glGetAttribLocation(program, "position"));
glCheck(glEnableVertexAttribArray(position));
glCheck(glVertexAttribPointer(position, size_of<glm::vec3>() / size_of<GLfloat>(), GL_FLOAT, GL_FALSE, size_of<Vertex>(), (GLvoid*)0));
GLint color;
glCheck(color = glGetAttribLocation(program, "color"));
glCheck(glEnableVertexAttribArray(color));
glCheck(glVertexAttribPointer(color, size_of<glm::vec4>() / size_of<GLfloat>(), GL_FLOAT, GL_FALSE, size_of<Vertex>(), (GLvoid*)size_of<glm::vec3>()));
GLint texCoord;
glCheck(texCoord = glGetAttribLocation(program, "texCoord"));
glCheck(glEnableVertexAttribArray(texCoord));
glCheck(glVertexAttribPointer(texCoord, size_of<glm::vec2>() / size_of<GLfloat>(), GL_FLOAT, GL_FALSE, size_of<Vertex>(), (GLvoid*)size_of<glm::vec3, glm::vec4>()));
glCheck(glBindBuffer(GL_ARRAY_BUFFER, 0));
glCheck(glBindVertexArray(0));
//glCheck(glEnable(GL_BLEND));
//glCheck(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
glCheck(glClearColor(0, 0, 1, 1));
while (window.isOpen())
{
glCheck(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
sf::Texture::bind(&font.getTexture(characterSize));
glCheck(glUniformMatrix4fv(glGetUniformLocation(program, "MVP"), 1, GL_FALSE, glm::value_ptr(orthoProjection)));
glCheck(glBindVertexArray(VAOs[Triangles]));
glCheck(glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, (GLvoid*)0));
glCheck(glBindVertexArray(0));
window.display();
pollEvent(window);
}
// clean resources
glCheck(glDeleteBuffers(NumBuffers, Buffers));
glCheck(glDeleteVertexArrays(NumVAOs, VAOs));
GLint numShaders = 0;
glCheck(glGetProgramiv(program, GL_ATTACHED_SHADERS, &numShaders));
std::vector<GLuint> shaders(numShaders);
glCheck(glGetAttachedShaders(program, numShaders, NULL, shaders.data()));
for (const auto& type : shaders) {
glCheck(glDetachShader(program, type));
}
glCheck(glDeleteProgram(program));
}
int main()
{
try
{
application();
}
catch (std::runtime_error& e)
{
std::cerr << "Exception: " << e.what() << std::endl;
std::cin.ignore();
return 1;
}
//std::cin.ignore();
}