Small program to quickly test OpenGL GLSL shaders.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

349 lines
8.1 KiB

#include <cassert>
#include <cstdlib>
#include <cstring>
#include <string>
#include <fstream>
#include <iostream>
#include <SDL2/SDL.h>
#include <glm/geometric.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include "shader_testing.h"
#include "dumpShader.inl"
SDL_Window *g_window = 0;
SDL_GLContext g_glContext;
SDL_DisplayMode g_display_mode;
transforms g_xforms;
mesh g_cube_mesh;
const u32 NUM_CUBES = 4;
gl_mesh g_gl_cubes[NUM_CUBES];
glm::vec3 g_cube_locs[NUM_CUBES] = {
glm::vec3(-10, 10, 0),
glm::vec3(-10, -10, 0),
glm::vec3( 10, 10, 0),
glm::vec3( 10, -10, 0),
};
void
openglDebugCallback(GLenum source,
GLenum type,
GLuint id,
GLenum severity,
GLsizei length,
const GLchar* message,
const void* userParam)
{
std::cout << ((type == GL_DEBUG_TYPE_ERROR) ? "Error" : "Debug")
<< (type == GL_DEBUG_TYPE_ERROR ? "** GL Error **" : "")
<< ", type: " << type
<< ", severity: " << severity
<< ", message: " << message << "\n";
}
bool
init()
{
g_window = SDL_CreateWindow(
"shader_testing",
SDL_WINDOWPOS_CENTERED_DISPLAY(0),
SDL_WINDOWPOS_CENTERED_DISPLAY(0),
1280,
720,
SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE);
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
std::cout << "error, sdl init: " << SDL_GetError() << "\n";
return false;
}
SDL_GL_SetSwapInterval(1);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS,
SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,
SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GetCurrentDisplayMode(0, &g_display_mode);
g_glContext = SDL_GL_CreateContext(g_window);
if (!g_glContext) {
std::cout << "error creating context\n";
return false;
}
if (glewInit()) {
std::cout << "error initializing opengl\n";
return false;
}
std::cout << "opengl vendor: " << glGetString(GL_VENDOR) << "\n";
std::cout << "opengl renderer: " << glGetString(GL_RENDERER) << "\n";
std::cout << "opengl version: " << glGetString(GL_VERSION) << "\n";
glEnable(GL_DEPTH_TEST);
glEnable(GL_LINE_SMOOTH);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glEnable (GL_DEBUG_OUTPUT);
glDebugMessageCallback((GLDEBUGPROC) openglDebugCallback, 0);
return g_window != nullptr;
}
const std::string
dumpTextFile(const char* filepath)
{
std::ifstream fs { filepath };
std::string s {
std::istreambuf_iterator<char>(fs),
std::istreambuf_iterator<char>()};
if (!fs || !fs.good())
std::cout << "error reading file, " << filepath << "\n";
return s;
}
bool
loadShaderProgram(const char* vs, const char* fs, shader* s)
{
std::string vert = dumpTextFile(vs);
std::string frag = dumpTextFile(fs);
if (vert.size() > 0 && frag.size() > 0) {
const char* vert_c = vert.c_str();
const char* frag_c = frag.c_str();
GLuint vs_id = glCreateShader(GL_VERTEX_SHADER);
GLuint fs_id = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(vs_id, 1, &vert_c, NULL);
glShaderSource(fs_id, 1, &frag_c, NULL);
glCompileShader(vs_id);
glCompileShader(fs_id);
s->prog_id = glCreateProgram();
glAttachShader(s->prog_id, vs_id);
glAttachShader(s->prog_id, fs_id);
glLinkProgram(s->prog_id);
glDetachShader(s->prog_id, vs_id);
glDetachShader(s->prog_id, fs_id);
glDeleteShader(vs_id);
glDeleteShader(fs_id);
GLint is_linked = 0;
glGetProgramiv(s->prog_id, GL_LINK_STATUS, &is_linked);
return (is_linked == GL_TRUE);
}
return false;
}
transforms
initXformUBO(shader* s,
float fov,
float near_clip_plane,
float aspect_ratio,
glm::vec3 cam_pos,
glm::vec3 look_pos)
{
transforms xforms;
xforms.view_xform = glm::lookAt(cam_pos, look_pos, glm::vec3(0, 1, 0));
xforms.proj_xform = glm::infinitePerspective(
glm::radians(fov), aspect_ratio, near_clip_plane);
xforms.normal_xform = glm::mat4(1);
glGenBuffers(1, &s->xforms_ubo_id);
glBindBufferBase(GL_UNIFORM_BUFFER, 0, s->xforms_ubo_id);
glBindBuffer(GL_UNIFORM_BUFFER, s->xforms_ubo_id);
glBufferData(GL_UNIFORM_BUFFER, sizeof(xforms), &xforms, GL_STATIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
return xforms;
};
mesh
initCubeMesh()
{
mesh m = {0};
m.num_vertices = 8;
m.vertices = (glm::vec3*) std::calloc(m.num_vertices, sizeof(glm::vec3));
m.vertices[0] = { -1, 1, -1 };
m.vertices[1] = { -1, -1, -1 };
m.vertices[2] = { 1, -1, -1 };
m.vertices[3] = { 1, 1, -1 };
m.vertices[4] = { -1, 1, 1 };
m.vertices[5] = { -1, -1, 1 };
m.vertices[6] = { 1, -1, 1 };
m.vertices[7] = { 1, 1, 1 };
m.num_indices = 36; // 6 sides, 2 tris per side, 3 verts per tri
m.indices = (u32*) std::calloc(m.num_indices, sizeof(u32));
u32 indices[36] = {
0, 1, 2, 0, 2, 3,
3, 2, 6, 3, 6, 7,
7, 6, 5, 7, 5, 4,
4, 5, 0, 4, 1, 0,
0, 3, 4, 0, 3, 7,
1, 2, 5, 2, 6, 5
};
std::memcpy(m.indices, indices, m.num_indices * sizeof(u32));
return m;
}
gl_mesh
loadGLMesh(shader* s, const mesh& m, GLenum draw_mode, const glm::vec3& pos)
{
gl_mesh gm = {0};
gm.num_indices = m.num_indices;
gm.draw_mode = draw_mode;
glUseProgram(s->prog_id);
glGenVertexArrays(1, &gm.vao_id);
glBindVertexArray(gm.vao_id);
gm.model_xform = (glm::mat4*) std::calloc(1, sizeof(glm::mat4));
*gm.model_xform = glm::translate(glm::mat4(1), pos);
gm.model_xform_id = glGetUniformLocation(s->prog_id, "model_xform");
glUniformMatrix4fv(
gm.model_xform_id, 1, GL_FALSE, (float*) gm.model_xform);
glGenBuffers(1, &gm.vert_buf_id);
glBindBuffer(GL_ARRAY_BUFFER, gm.vert_buf_id);
glBufferData(GL_ARRAY_BUFFER,
m.num_vertices * 3 * sizeof(GLfloat),
m.vertices,
GL_STATIC_DRAW);
glVertexAttribPointer(POSITION, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(POSITION);
glGenBuffers(1, &gm.idx_buf_id);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gm.idx_buf_id);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
m.num_indices * sizeof(GLuint),
m.indices,
GL_STATIC_DRAW);
glBindVertexArray(0);
glUseProgram(0);
return gm;
}
void
loop(shader* s, gl_mesh* mesh_arr, u32 num_meshes)
{
u32 delay = 60;
u32 frame_start, frame_time;
bool running = true;
SDL_Event e;
while (running) {
frame_start = SDL_GetTicks();
while (SDL_PollEvent(&e)) {
if (e.type == SDL_QUIT ||
(e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE))
{
running = false;
break;
}
}
glClearColor(0.2, 0.2, 0.2, 1);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glUseProgram(s->prog_id);
glBindBuffer(GL_UNIFORM_BUFFER, s->xforms_ubo_id);
for (u32 i = 0; i < num_meshes; i++) {
const gl_mesh& m = mesh_arr[i];
*m.model_xform = glm::rotate(
*m.model_xform, (float) M_PI / 60, glm::vec3(0, 1, 0));
glBindVertexArray(m.vao_id);
glUniformMatrix4fv(
m.model_xform_id, 1, GL_FALSE, (float*) m.model_xform);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m.idx_buf_id);
glDrawElements(m.draw_mode, m.num_indices, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
}
SDL_GL_SwapWindow(g_window);
glUseProgram(0);
frame_time = SDL_GetTicks() - frame_start;
if (delay > frame_time)
SDL_Delay(delay - frame_time);
}
}
void
quit(shader* s)
{
glDeleteProgram(s->prog_id);
SDL_GL_DeleteContext(g_glContext);
SDL_DestroyWindow(g_window);
SDL_Quit();
for (u32 i = 0; i < NUM_CUBES; i++) {
gl_mesh& c = g_gl_cubes[i];
if (c.model_xform != nullptr)
free(c.model_xform);
}
mesh& cm = g_cube_mesh;
if (cm.vertices != nullptr)
free(cm.vertices);
if (cm.normals != nullptr)
free(cm.normals);
if (cm.uvs != nullptr)
free(cm.uvs);
if (cm.colors != nullptr)
free(cm.colors);
if (cm.indices != nullptr)
free(cm.indices);
}
int
main()
{
if (!init())
return 1;
shader s = {0};
if (loadShaderProgram("data/shader.vert", "data/shader.frag", &s)) {
dumpShader(s.prog_id);
g_xforms = initXformUBO(&s);
g_cube_mesh = initCubeMesh();
for (u32 i = 0; i < NUM_CUBES; i++) {
g_gl_cubes[i] =
loadGLMesh(&s, g_cube_mesh, GL_TRIANGLES, g_cube_locs[i]);
assert(g_gl_cubes[i].vao_id != 0);
}
loop(&s, g_gl_cubes, NUM_CUBES);
quit(&s);
return 0;
}
std::cout << "error loading shader program\n";
quit(&s);
return 1;
}