Skip to content

Instantly share code, notes, and snippets.

@MORTAL2000
Last active August 5, 2016 08:20
Show Gist options
  • Save MORTAL2000/08619877332ad2ac3f385feae82af6bb to your computer and use it in GitHub Desktop.
Save MORTAL2000/08619877332ad2ac3f385feae82af6bb to your computer and use it in GitHub Desktop.
#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
(
const vec4 zeroVec4 = vec4(0);
in vec4 color0;
in vec2 texCoord0;
uniform sampler2D diffuse;
uniform vec4 color;
out vec4 finalColor;
void main()
{
vec4 texelColor = texture(diffuse, texCoord0);
bool hasUniformColor = color != zeroVec4;
bool hasUniformDiffuse = texelColor.x != zeroVec4.x;
if (hasUniformDiffuse && hasUniformColor)
finalColor = texelColor * color0 * color;
else if (hasUniformDiffuse && !hasUniformColor)
finalColor = texelColor * color0;
else if (!hasUniformDiffuse && hasUniformColor)
finalColor = color0 * color;
else if (!hasUniformDiffuse && !hasUniformColor)
finalColor = color0;
}
);
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::Texture texture;
if (!texture.loadFromFile("test.png")) {
throw std::runtime_error("Unable to load texture");
}
float left = 0;
float top = 0;
float right = left + 200;
float bottom = top + 200;
float x = 800.f / 2.f - right / 2.f;
float y = 600.f / 2.f - bottom / 2.f;
std::vector<Vertex> vertices = {
{ glm::vec3(x + left, y + top, 0), glm::vec4(1), glm::vec2(0, 0) },
{ glm::vec3(x + left, y + bottom, 0), glm::vec4(1), glm::vec2(0, 1) },
{ glm::vec3(x + right, y + top, 0), glm::vec4(1), glm::vec2(1, 0) },
{ glm::vec3(x + right, y + bottom, 0), glm::vec4(1), glm::vec2(1, 1) },
};
std::vector<GLuint> indices = { 0, 1, 2, 2, 1, 3 };
//std::vector<Vertex> vertices = {
// { glm::vec3(x + left, y + top, 0), glm::vec4(1), glm::vec2(0, 0) },
// { glm::vec3(x + left, y + bottom, 0), glm::vec4(1), glm::vec2(0, 1) },
// { glm::vec3(x + right, y + bottom, 0), glm::vec4(1), glm::vec2(1, 1) },
// { glm::vec3(x + right, y + top, 0), glm::vec4(1), glm::vec2(1, 0) },
//};
//std::vector<GLuint> indices = { 0, 1, 3, 3, 1, 2 };
window.setActive();
std::vector<GLuint> VAOs(NumVAOs);
std::vector<GLuint> Buffers(NumBuffers);
glCheck(glGenVertexArrays(NumVAOs, VAOs.data()));
glCheck(glGenBuffers(NumBuffers, Buffers.data()));
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);
GLint position;
glCheck(position = glGetAttribLocation(program, "position")); // NOTE: only if variable is used in shader source
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(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); // NOTE: it is better never turns it off, might help with meshes
glCheck(glBindVertexArray(0));
glCheck(glClearColor(0, 0, 1, 1));
while (window.isOpen())
{
glCheck(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
glCheck(glUseProgram(program));
//glCheck(glUniform4f(glGetUniformLocation(program, "color"), 0, 1, 0, 1));
glCheck(glUniform4fv(glGetUniformLocation(program, "color"),1, glm::value_ptr(glm::vec4(0,1,0,1))));
//glCheck(glUniform1i(glGetUniformLocation(program, "diffuse"), texture.getNativeHandle()));
//glCheck(glActiveTexture(GL_TEXTURE0 + texture.getNativeHandle()));
//sf::Texture::bind(&texture);
glCheck(glBindTexture(GL_TEXTURE_2D, texture.getNativeHandle()));
//glCheck(glActiveTexture(GL_TEXTURE0));
glCheck(glUniformMatrix4fv(glGetUniformLocation(program, "MVP"), 1, GL_FALSE, glm::value_ptr(orthoProjection)));
glCheck(glBindVertexArray(VAOs[Triangles]));
//glCheck(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Buffers[IndexBuffer]));
glCheck(glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0));
//glCheck(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
glCheck(glBindVertexArray(0));
glCheck(glUseProgram(0));
glCheck(glBindTexture(GL_TEXTURE_2D, 0));
window.display();
pollEvent(window);
}
// clean resources
glCheck(glDeleteBuffers(NumBuffers, Buffers.data()));
glCheck(glDeleteVertexArrays(NumVAOs, VAOs.data()));
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();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment