@@ -1,5 +1,7 @@ | |||
add_library(cygnet SHARED | |||
src/GlProgram.cc | |||
src/BuiltinShaders.cc | |||
src/glutil.cc | |||
src/GlWrappers.cc | |||
src/Window.cc) | |||
target_link_libraries(cygnet PUBLIC SDL2 GLESv2) | |||
target_include_directories(cygnet | |||
@@ -9,3 +11,7 @@ target_include_directories(cygnet | |||
add_executable(cygnet-hello-triangle | |||
samples/hello-triangle/hello-triangle.cc) | |||
target_link_libraries(cygnet-hello-triangle PUBLIC cygnet) | |||
add_executable(cygnet-hello-texture | |||
samples/hello-texture/hello-texture.cc) | |||
target_link_libraries(cygnet-hello-texture PUBLIC cygnet) |
@@ -0,0 +1,15 @@ | |||
#include <optional> | |||
#include "GlWrappers.h" | |||
namespace Cygnet { | |||
struct BuiltinShaders { | |||
BuiltinShaders(); | |||
GlShader textureVertex; | |||
GlShader textureFragment; | |||
GlShader whiteFragment; | |||
}; | |||
} |
@@ -16,14 +16,13 @@ public: | |||
FRAGMENT, | |||
}; | |||
GlShader(const char *source, Type type); | |||
GlShader(Type type, const char *source); | |||
~GlShader(); | |||
GLuint id() const { return id_; } | |||
private: | |||
GLuint id_; | |||
bool valid_ = false; | |||
}; | |||
class GlProgram: NonCopyable { | |||
@@ -38,11 +37,11 @@ public: | |||
void use() { glUseProgram(id_); } | |||
GLuint id() { return id_; } | |||
GLuint getLocation(const char *name) { return glGetAttribLocation(id_, name); } | |||
GLint attribLocation(const char *name) { return glGetAttribLocation(id_, name); } | |||
GLint uniformLocation(const char *name) { return glGetAttribLocation(id_, name); } | |||
private: | |||
GLuint id_; | |||
bool valid_ = false; | |||
}; | |||
} |
@@ -0,0 +1,9 @@ | |||
#pragma once | |||
namespace Cygnet { | |||
const char *glErrorString(int err); | |||
void glCheck(); | |||
void sdlAssert(bool val); | |||
} |
@@ -0,0 +1,88 @@ | |||
#include <cygnet/Window.h> | |||
#include <cygnet/GlWrappers.h> | |||
#include <cygnet/BuiltinShaders.h> | |||
#include <cygnet/glutil.h> | |||
#include <iostream> | |||
int main() { | |||
SDL_Init(SDL_INIT_VIDEO); | |||
Cygnet::Deferred<SDL_Quit> sdl; | |||
Cygnet::Window win("Hello Triangle", 640, 480); | |||
Cygnet::BuiltinShaders shaders; | |||
Cygnet::GlProgram program(shaders.textureVertex, shaders.textureFragment); | |||
program.use(); | |||
GLfloat vertexes[] = { | |||
-0.5f, -0.5f, 0.0f, // pos 0: top left | |||
0.0f, 0.0f, // tex 0: top left | |||
-0.5f, 0.5f, 0.0f, // pos 1: bottom left | |||
0.0f, 1.0f, // tex 1: bottom left | |||
0.5f, 0.5f, 0.0f, // pos 2: bottom right | |||
1.0f, 1.0f, // tex 2: bottom right | |||
0.5f, -0.5f, 0.0f, // pos 3: top right | |||
1.0f, 0.0f, // tex 3: top right | |||
}; | |||
GLushort indexes[] = { | |||
0, 1, 2, // top left -> bottom left -> bottom right | |||
2, 3, 0, // bottom right -> top right -> top left | |||
}; | |||
uint8_t image[] = { | |||
0xff, 0xff, 0xff, 0xaa, 0xaa, 0xaa, 0xff, 0x00, 0xba, | |||
0xaa, 0xaa, 0xaa, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, | |||
0xa0, 0x55, 0x77, 0x00, 0xf0, 0x0f, 0xff, 0x00, 0xff, | |||
}; | |||
GLuint texId; | |||
glGenTextures(1, &texId); | |||
glBindTexture(GL_TEXTURE_2D, texId); | |||
Cygnet::glCheck(); | |||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 3, 3, 0, GL_RGB, GL_UNSIGNED_BYTE, image); | |||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |||
Cygnet::glCheck(); | |||
GLint positionLoc = program.attribLocation("position"); | |||
GLint texCoordLoc = program.attribLocation("texCoord"); | |||
GLint texLoc = program.uniformLocation("tex"); | |||
// Draw loop | |||
while (true) { | |||
SDL_Event evt; | |||
while (SDL_PollEvent(&evt)) { | |||
switch (evt.type) { | |||
case SDL_QUIT: | |||
goto exit; | |||
break; | |||
} | |||
} | |||
win.clear(); | |||
glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), vertexes); | |||
glVertexAttribPointer(texCoordLoc, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), &vertexes[3]); | |||
Cygnet::glCheck(); | |||
glEnableVertexAttribArray(positionLoc); | |||
glEnableVertexAttribArray(texCoordLoc); | |||
Cygnet::glCheck(); | |||
glActiveTexture(GL_TEXTURE0); | |||
glBindTexture(GL_TEXTURE_2D, texId); | |||
glUniform1i(texLoc, 0); | |||
Cygnet::glCheck(); | |||
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indexes); | |||
Cygnet::glCheck(); | |||
win.flip(); | |||
} | |||
exit: | |||
return EXIT_SUCCESS; | |||
} |
@@ -1,5 +1,5 @@ | |||
#include <cygnet/Window.h> | |||
#include <cygnet/GlProgram.h> | |||
#include <cygnet/GlWrappers.h> | |||
#include <iostream> | |||
const char *vertexSource = R"( | |||
@@ -21,10 +21,10 @@ int main() { | |||
Cygnet::Deferred<SDL_Quit> sdl; | |||
Cygnet::Window win("Hello Triangle", 640, 480); | |||
Cygnet::GlShader vertex(vertexSource, Cygnet::GlShader::Type::VERTEX); | |||
Cygnet::GlShader fragment(fragmentSource, Cygnet::GlShader::Type::FRAGMENT); | |||
Cygnet::GlShader vertex(Cygnet::GlShader::Type::VERTEX, vertexSource); | |||
Cygnet::GlShader fragment(Cygnet::GlShader::Type::FRAGMENT, fragmentSource); | |||
Cygnet::GlProgram program(vertex, fragment); | |||
GLuint positionAttrib = program.getLocation("position"); | |||
GLuint positionAttrib = program.attribLocation("position"); | |||
program.use(); | |||
GLfloat vertixes[] = { |
@@ -0,0 +1,31 @@ | |||
#include "BuiltinShaders.h" | |||
namespace Cygnet { | |||
BuiltinShaders::BuiltinShaders(): | |||
textureVertex(GlShader::Type::VERTEX, R"( | |||
attribute vec4 position; | |||
attribute vec2 texCoord; | |||
varying vec2 v_texCoord; | |||
void main() { | |||
gl_Position = position; | |||
v_texCoord = texCoord; | |||
} | |||
)"), | |||
textureFragment(GlShader::Type::FRAGMENT, R"( | |||
precision mediump float; | |||
varying vec2 v_texCoord; | |||
uniform sampler2D tex; | |||
void main() { | |||
gl_FragColor = texture2D(tex, v_texCoord); | |||
} | |||
)"), | |||
whiteFragment(GlShader::Type::FRAGMENT, R"( | |||
precision mediump float; | |||
void main() { | |||
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); | |||
} | |||
)") {} | |||
} |
@@ -1,71 +1,75 @@ | |||
#include "GlProgram.h" | |||
#include "GlWrappers.h" | |||
#include <iostream> | |||
#include <stdexcept> | |||
#include "glutil.h" | |||
namespace Cygnet { | |||
GlShader::GlShader(const char *source, Type type) { | |||
GlShader::GlShader(Type type, const char *source) { | |||
switch (type) { | |||
case Type::VERTEX: | |||
id_ = glCreateShader(GL_VERTEX_SHADER); | |||
glCheck(); | |||
std::cerr << "compiling vertex shader...\n"; | |||
break; | |||
case Type::FRAGMENT: | |||
id_ = glCreateShader(GL_FRAGMENT_SHADER); | |||
glCheck(); | |||
std::cerr << "compiling fragment shader...\n"; | |||
break; | |||
} | |||
glShaderSource(id_, 1, &source, NULL); | |||
glCheck(); | |||
glCompileShader(id_); | |||
glCheck(); | |||
char log[4096]; | |||
GLsizei length; | |||
GLsizei length = 0; | |||
glGetShaderInfoLog(id_, sizeof(log), &length, log); | |||
glCheck(); | |||
if (length != 0) { | |||
std::cerr << "Shader compile info:\n" << log << '\n'; | |||
std::cerr << "Shader compile info (" << length << "):\n" << log << '\n'; | |||
} | |||
GLint status; | |||
GLint status = 0; | |||
glGetShaderiv(id_, GL_COMPILE_STATUS, &status); | |||
glCheck(); | |||
if (status == GL_FALSE) { | |||
id_ = -1; | |||
throw std::runtime_error("GL shader compilation failed."); | |||
} | |||
valid_ = true; | |||
} | |||
GlShader::~GlShader() { | |||
if (valid_) { | |||
glDeleteShader(id_); | |||
} | |||
glDeleteShader(id_); | |||
glCheck(); | |||
} | |||
void GlProgram::link() { | |||
std::cout << "link\n"; | |||
glLinkProgram(id_); | |||
glCheck(); | |||
char log[4096]; | |||
GLsizei length; | |||
GLsizei length = 0; | |||
glGetProgramInfoLog(id_, sizeof(log), &length, log); | |||
glCheck(); | |||
if (length != 0) { | |||
std::cerr << "Program link info:\n" << log << '\n'; | |||
} | |||
GLint status; | |||
GLint status = 0; | |||
glGetProgramiv(id_, GL_LINK_STATUS, &status); | |||
glCheck(); | |||
if (status == GL_FALSE) { | |||
id_ = -1; | |||
throw std::runtime_error("GL program link failed."); | |||
} | |||
valid_ = true; | |||
} | |||
GlProgram::~GlProgram() { | |||
if (valid_) { | |||
glDeleteProgram(id_); | |||
} | |||
glDeleteProgram(id_); | |||
} | |||
} |
@@ -1,7 +1,8 @@ | |||
#include "Window.h" | |||
#include <SDL_opengles2.h> | |||
#include <assert.h> | |||
#include "glutil.h" | |||
namespace Cygnet { | |||
@@ -16,10 +17,11 @@ Window::Window(const char *name, int width, int height) { | |||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); | |||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); | |||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); | |||
glCheck(); | |||
assert(glctx_ = SDL_GL_CreateContext(win_.get())); | |||
sdlAssert(glctx_ = SDL_GL_CreateContext(win_.get())); | |||
makeCurrent(); | |||
assert(SDL_GL_SetSwapInterval(1) == 0); | |||
glCheck(); | |||
} | |||
Window::~Window() { |
@@ -0,0 +1,36 @@ | |||
#include "glutil.h" | |||
#include <SDL.h> | |||
#include <SDL_opengles2.h> | |||
#include <stdexcept> | |||
namespace Cygnet { | |||
const char *glErrorString(int err) { | |||
#define errcase(x) case x: return #x | |||
switch (err) { | |||
errcase(GL_NO_ERROR); | |||
errcase(GL_INVALID_ENUM); | |||
errcase(GL_INVALID_VALUE); | |||
errcase(GL_INVALID_OPERATION); | |||
errcase(GL_INVALID_FRAMEBUFFER_OPERATION); | |||
errcase(GL_OUT_OF_MEMORY); | |||
default: return "(unknown)"; | |||
} | |||
#undef errcase | |||
} | |||
void glCheck() { | |||
GLenum err = glGetError(); | |||
if (err != GL_NO_ERROR) { | |||
throw std::runtime_error(glErrorString(err)); | |||
} | |||
} | |||
void sdlAssert(bool val) { | |||
if (!val) { | |||
throw std::runtime_error(SDL_GetError()); | |||
} | |||
} | |||
} |