--- /dev/null
+*.d
+*.o
+*.swp
+*.swo
+blank
--- /dev/null
+CXX = g++ --std=c++11
+LDXX = g++
+
+LIBS = sdl2 glew
+
+PKGFLAGS := $(shell pkg-config --cflags $(LIBS))
+PKGLIBS := $(shell pkg-config --libs $(LIBS))
+
+CPPFLAGS ?=
+CPPFLAGS += $(PKGFLAGS)
+CXXFLAGS ?=
+CXXFLAGS += -Wall
+LDXXFLAGS ?=
+LDXXFLAGS += $(PKGLIBS)
+
+SRC = $(wildcard src/*.cpp)
+OBJ = $(SRC:.cpp=.o)
+DEP = $(SRC:.cpp=.d)
+BIN = blank
+
+all: $(BIN)
+
+clean:
+ rm -f $(BIN) $(OBJ) $(DEP)
+
+.PHONY: all clean
+
+-include $(DEP)
+
+$(BIN): $(OBJ)
+ $(LDXX) -o $@ $(CXXFLAGS) $(LDXXFLAGS) $^
+
+%.o: %.cpp
+ $(CXX) -c $(CPPFLAGS) $(CXXFLAGS) -o $@ -MMD -MP -MF"$*".d -MT"$@" $<
--- /dev/null
+#include "init.hpp"
+
+#include <algorithm>
+#include <SDL.h>
+#include <stdexcept>
+#include <string>
+#include <GL/glew.h>
+
+
+namespace {
+
+void sdl_error(std::string msg) {
+ const char *error = SDL_GetError();
+ if (*error != '\0') {
+ msg += ": ";
+ msg += error;
+ SDL_ClearError();
+ }
+ throw std::runtime_error(msg);
+}
+
+}
+
+namespace blank {
+
+InitSDL::InitSDL() {
+ if (SDL_Init(SDL_INIT_VIDEO) != 0) {
+ sdl_error("SDL_Init(SDL_INIT_VIDEO)");
+ }
+}
+
+InitSDL::~InitSDL() {
+ SDL_Quit();
+}
+
+
+InitGL::InitGL() {
+ if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3) != 0) {
+ sdl_error("SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3)");
+ }
+ if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3) != 0) {
+ sdl_error("SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3)");
+ }
+ if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE) != 0) {
+ sdl_error("SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE)");
+ }
+
+ if (SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1) != 0) {
+ sdl_error("SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1)");
+ }
+}
+
+InitGL::~InitGL() {
+
+}
+
+
+Window::Window()
+: handle(SDL_CreateWindow(
+ "blank",
+ SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
+ 960, 600,
+ SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE
+)) {
+ if (!handle) {
+ sdl_error("SDL_CreateWindow");
+ }
+}
+
+Window::~Window() {
+ SDL_DestroyWindow(handle);
+}
+
+GLContext Window::CreateContext() {
+ return GLContext(handle);
+}
+
+void Window::Flip() {
+ SDL_GL_SwapWindow(handle);
+}
+
+
+GLContext::GLContext(SDL_Window *win)
+: handle(SDL_GL_CreateContext(win)) {
+ if (!handle) {
+ sdl_error("SDL_GL_CreateContext");
+ }
+}
+
+GLContext::~GLContext() {
+ if (handle) {
+ SDL_GL_DeleteContext(handle);
+ }
+}
+
+
+GLContext::GLContext(GLContext &&other)
+: handle(other.handle) {
+ other.handle = nullptr;
+}
+
+GLContext &GLContext::operator =(GLContext &&other) {
+ std::swap(handle, other.handle);
+ return *this;
+}
+
+void GLContext::EnableVSync() {
+ if (SDL_GL_SetSwapInterval(1) != 0) {
+ sdl_error("SDL_GL_SetSwapInterval");
+ }
+}
+
+
+InitGLEW::InitGLEW() {
+ glewExperimental = GL_TRUE;
+ GLenum glew_err = glewInit();
+ if (glew_err != GLEW_OK) {
+ std::string msg("glewInit: ");
+ const GLubyte *errBegin = glewGetErrorString(glew_err);
+ const GLubyte *errEnd = errBegin;
+ while (*errEnd != '\0') {
+ ++errEnd;
+ }
+ msg.append(errBegin, errEnd);
+ throw std::runtime_error(msg);
+ }
+}
+
+InitGLEW::~InitGLEW() {
+
+}
+
+}
--- /dev/null
+#ifndef BLANK_INIT_HPP_
+#define BLANK_INIT_HPP_
+
+#include <SDL.h>
+
+
+namespace blank {
+
+class GLContext;
+
+
+class InitSDL {
+
+public:
+ InitSDL();
+ ~InitSDL();
+
+ InitSDL(const InitSDL &) = delete;
+ InitSDL &operator =(const InitSDL &) = delete;
+
+};
+
+
+class InitGL {
+
+public:
+ InitGL();
+ ~InitGL();
+
+ InitGL(const InitGL &) = delete;
+ InitGL &operator =(const InitGL &) = delete;
+
+};
+
+
+class Window {
+
+public:
+ Window();
+ ~Window();
+
+ Window(const Window &) = delete;
+ Window &operator =(const Window &) = delete;
+
+ GLContext CreateContext();
+
+ void Flip();
+
+private:
+ SDL_Window *handle;
+
+};
+
+
+class GLContext {
+
+public:
+ explicit GLContext(SDL_Window *);
+ ~GLContext();
+
+ GLContext(GLContext &&);
+ GLContext &operator =(GLContext &&);
+
+ GLContext(const GLContext &) = delete;
+ GLContext &operator =(const GLContext &) = delete;
+
+ static void EnableVSync();
+
+private:
+ SDL_GLContext handle;
+
+};
+
+
+class InitGLEW {
+
+public:
+ InitGLEW();
+ ~InitGLEW();
+
+ InitGLEW(const InitGLEW &) = delete;
+ InitGLEW &operator =(const InitGLEW &) = delete;
+
+};
+
+}
+
+#endif
--- /dev/null
+#include <iostream>
+#include <SDL.h>
+#include <GL/glew.h>
+#include <glm/glm.hpp>
+#include <glm/gtc/matrix_transform.hpp>
+
+#include "init.hpp"
+#include "shader.hpp"
+
+using namespace std;
+using namespace blank;
+
+
+constexpr GLfloat vtx_coords[] = {
+ -1.0f, -1.0f, -1.0f,
+ 1.0f, -1.0f, -1.0f,
+ 0.0f, 1.0f, -1.0f,
+};
+
+
+int main(int argc, char *argv[]) {
+
+ InitSDL init_sdl;
+ InitGL init_gl;
+ Window window;
+
+ GLContext ctx = window.CreateContext();
+ InitGLEW init_glew;
+ GLContext::EnableVSync();
+
+
+ Shader vtx_shader(GL_VERTEX_SHADER);
+ vtx_shader.Source(
+ "#version 330 core\n"
+ "layout(location = 0) in vec3 vertexPosition_modelspace;\n"
+ "uniform mat4 MVP;\n"
+ "void main() {\n"
+ "vec4 v = vec4(vertexPosition_modelspace, 1);\n"
+ "gl_Position = MVP * v;\n"
+ "}\n"
+ );
+ vtx_shader.Compile();
+
+ if (!vtx_shader.Compiled()) {
+ cerr << "vertex shader compile error" << endl;
+ vtx_shader.Log(cerr);
+ return 4;
+ }
+
+ Shader frag_shader(GL_FRAGMENT_SHADER);
+ frag_shader.Source(
+ "#version 330 core\n"
+ "out vec3 color;\n"
+ "void main() {\n"
+ "color = vec3(1, 1, 1);\n"
+ "}\n"
+ );
+ frag_shader.Compile();
+
+ if (!frag_shader.Compiled()) {
+ cerr << "fragment shader compile error" << endl;
+ frag_shader.Log(cerr);
+ return 4;
+ }
+
+
+ Program program;
+ program.Attach(vtx_shader);
+ program.Attach(frag_shader);
+ program.Link();
+
+ if (!program.Linked()) {
+ cerr << "program link error" << endl;
+ program.Log(cerr);
+ return 4;
+ }
+
+
+ GLuint VertexArrayID;
+ glGenVertexArrays(1, &VertexArrayID);
+ glBindVertexArray(VertexArrayID);
+
+
+ GLuint vtx_buf;
+ glGenBuffers(1, &vtx_buf);
+ glBindBuffer(GL_ARRAY_BUFFER, vtx_buf);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(vtx_coords), vtx_coords, GL_STATIC_DRAW);
+
+
+ glm::mat4 projection = glm::perspective(
+ 45.0f, // FOV in degrees
+ 1.0f, // aspect ratio
+ 0.1f, // near clip
+ 100.0f // far clip
+ );
+ glm::mat4 view = glm::lookAt(
+ glm::vec3(0, 0, 0), // observer
+ glm::vec3(0, 0, -1), // target
+ glm::vec3(0, 1, 0) // up
+ );
+ glm::mat4 model(1.0f); // identity: no transformation
+ glm::mat4 mvp = projection * view * model;
+
+ GLuint mvp_id = program.UniformLocation("MVP");
+
+
+ glClearColor(0.0, 0.0, 0.0, 1.0);
+
+
+ bool running = true;
+ Uint32 last = SDL_GetTicks();
+ while (running) {
+ Uint32 now = SDL_GetTicks();
+ int delta = now - last;
+
+ SDL_Event event;
+ while (SDL_PollEvent(&event)) {
+ switch (event.type) {
+ case SDL_QUIT:
+ running = false;
+ break;
+ default:
+ break;
+ }
+ }
+
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ program.Use();
+
+ glUniformMatrix4fv(mvp_id, 1, GL_FALSE, &mvp[0][0]);
+
+ glEnableVertexAttribArray(0);
+ glBindBuffer(GL_ARRAY_BUFFER, vtx_buf);
+ glVertexAttribPointer(
+ 0, // attribute 0 (for shader)
+ 3, // size
+ GL_FLOAT, // type
+ GL_FALSE, // normalized
+ 0, // stride
+ nullptr // offset
+ );
+ glDrawArrays(
+ GL_TRIANGLES, // how
+ 0, // start
+ 3 // len
+ );
+ glDisableVertexAttribArray(0);
+
+ window.Flip();
+
+ last = now;
+ }
+
+ return 0;
+
+}
--- /dev/null
+#include "shader.hpp"
+
+#include <algorithm>
+#include <memory>
+#include <ostream>
+#include <stdexcept>
+#include <string>
+
+
+namespace {
+
+void gl_error(std::string msg) {
+ const GLubyte *errBegin = gluErrorString(glGetError());
+ if (errBegin && *errBegin != '\0') {
+ const GLubyte *errEnd = errBegin;
+ while (*errEnd != '\0') {
+ ++errEnd;
+ }
+ msg += ": ";
+ msg.append(errBegin, errEnd);
+ }
+ throw std::runtime_error(msg);
+}
+
+}
+
+namespace blank {
+
+Shader::Shader(GLenum type)
+: handle(glCreateShader(type)) {
+ if (handle == 0) {
+ gl_error("glCreateShader");
+ }
+}
+
+Shader::~Shader() {
+ if (handle != 0) {
+ glDeleteShader(handle);
+ }
+}
+
+Shader::Shader(Shader &&other)
+: handle(other.handle) {
+ other.handle = 0;
+}
+
+Shader &Shader::operator =(Shader &&other) {
+ std::swap(handle, other.handle);
+ return *this;
+}
+
+
+void Shader::Source(const GLchar *src) {
+ const GLchar* src_arr[] = { src };
+ glShaderSource(handle, 1, src_arr, nullptr);
+}
+
+void Shader::Compile() {
+ glCompileShader(handle);
+}
+
+bool Shader::Compiled() const {
+ GLint compiled = GL_FALSE;
+ glGetShaderiv(handle, GL_COMPILE_STATUS, &compiled);
+ return compiled == GL_TRUE;
+}
+
+void Shader::Log(std::ostream &out) const {
+ int log_len = 0, max_len = 0;
+ glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &max_len);
+ std::unique_ptr<char[]> log(new char[max_len]);
+ glGetShaderInfoLog(handle, max_len, &log_len, log.get());
+ out.write(log.get(), log_len);
+}
+
+
+void Shader::AttachToProgram(GLuint id) const {
+ glAttachShader(id, handle);
+}
+
+
+Program::Program()
+: handle(glCreateProgram()) {
+ if (handle == 0) {
+ gl_error("glCreateProgram");
+ }
+}
+
+Program::~Program() {
+ if (handle != 0) {
+ glDeleteProgram(handle);
+ }
+}
+
+
+void Program::Attach(Shader &shader) {
+ shader.AttachToProgram(handle);
+}
+
+void Program::Link() {
+ glLinkProgram(handle);
+}
+
+bool Program::Linked() const {
+ GLint linked = GL_FALSE;
+ glGetProgramiv(handle, GL_LINK_STATUS, &linked);
+ return linked == GL_TRUE;
+}
+
+void Program::Log(std::ostream &out) const {
+ int log_len = 0, max_len = 0;
+ glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &max_len);
+ std::unique_ptr<char[]> log(new char[max_len]);
+ glGetProgramInfoLog(handle, max_len, &log_len, log.get());
+ out.write(log.get(), log_len);
+}
+
+
+GLint Program::UniformLocation(const GLchar *name) const {
+ return glGetUniformLocation(handle, name);
+}
+
+}
--- /dev/null
+#ifndef BLANK_SHADER_HPP_
+#define BLANK_SHADER_HPP_
+
+#include <iosfwd>
+#include <GL/glew.h>
+
+
+namespace blank {
+
+class Shader {
+
+public:
+ explicit Shader(GLenum type);
+ ~Shader();
+
+ Shader(Shader &&);
+ Shader &operator =(Shader &&);
+
+ Shader(const Shader &) = delete;
+ Shader &operator =(const Shader &) = delete;
+
+ void Source(const GLchar *src);
+ void Compile();
+ bool Compiled() const;
+ void Log(std::ostream &) const;
+
+ void AttachToProgram(GLuint id) const;
+
+private:
+ GLuint handle;
+
+};
+
+
+class Program {
+
+public:
+ Program();
+ ~Program();
+
+ Program(const Program &) = delete;
+ Program &operator =(const Program &) = delete;
+
+ void Attach(Shader &);
+ void Link();
+ bool Linked() const;
+ void Log(std::ostream &) const;
+
+ GLint UniformLocation(const GLchar *name) const;
+
+ void Use() const { glUseProgram(handle); }
+
+private:
+ GLuint handle;
+
+};
+
+}
+
+#endif