commit 3afa3d9cc86f8c1b9470c821a02fc620a507a6c8 Author: cinnaboot Date: Tue Nov 16 10:58:25 2021 -0500 Initial commit new file: .gitignore new file: Makefile new file: dumpShader.inl new file: main.cpp new file: shader_testing new file: shader_testing.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d9fa48d --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ + +tags +*.d +*.bin +data/ +build/ + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c452c1d --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ + +SHELL = /bin/sh +CXX = g++ +CXXFLAGS = -std=c++11 -g -ggdb3 -Wall -I/usr/include/SDL2 +LDFLAGS = -lSDL2 -lGLEW -lGL +BIN = shader_testing + +all: + $(CXX) $(CXXFLAGS) -MMD main.cpp -o $(BIN) $(LDFLAGS) +.PHONY: all + +clean: + rm -rf $(BIN) +.PHONY: clean + diff --git a/dumpShader.inl b/dumpShader.inl new file mode 100644 index 0000000..ed44f56 --- /dev/null +++ b/dumpShader.inl @@ -0,0 +1,61 @@ + +#include + +#include "shader_testing.h" + + +const char* gl_enum_to_string(GLenum e); + + +void +dumpShader(GLuint prog_id) +{ + std::cout << "dumping shader info, program id: " << prog_id << "\n"; + + GLint active_uniforms; + GLint active_uniform_blocks; + GLint active_attribs; + + // NOTE: unused uniforms/attributes get optimized away + // https://www.khronos.org/opengl/wiki/Program_Introspection#Attributes + // https://stackoverflow.com/questions/54811319/how-to-get-from-glgetattriblocation-to-glgetactiveattrib-index + glGetProgramiv(prog_id, GL_ACTIVE_UNIFORMS, &active_uniforms); + glGetProgramiv(prog_id, GL_ACTIVE_UNIFORM_BLOCKS, &active_uniform_blocks); + glGetProgramiv(prog_id, GL_ACTIVE_ATTRIBUTES, &active_attribs); + + printf("active uniforms: %d\n", active_uniforms); + printf("active uniform blocks: %d\n", active_uniform_blocks); + printf("active attributes: %d\n", active_attribs); + + GLchar uni_name[256]; + GLsizei length; + GLint size; + GLenum type; + + for (int i = 0; i < active_uniforms; i++) { + glGetActiveUniform(prog_id, i, sizeof(uni_name), + &length, &size, &type, uni_name); + printf("uniform idx: %d, len: %d, type: %s, name: %s \n", + i, length, gl_enum_to_string(type), uni_name); + } + + for (int i = 0; i < active_attribs; i++) { + glGetActiveAttrib(prog_id, i, sizeof(uni_name), + &length, &size, &type, uni_name); + printf("attribute idx: %d, len: %d, type: %s, name: %s \n", + i, length, gl_enum_to_string(type), uni_name); + } + +} + +const char* +gl_enum_to_string(GLenum e) +{ + switch (e) { + case 0x8b5c: return "GL_FLOAT_MAT4"; + case 0x8b51: return "GL_FLOAT_VEC3"; + + default: return "???"; + } +} + diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..f35dc02 --- /dev/null +++ b/main.cpp @@ -0,0 +1,349 @@ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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(fs), + std::istreambuf_iterator()}; + + 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; +} + diff --git a/shader_testing b/shader_testing new file mode 100755 index 0000000..7b01d0e Binary files /dev/null and b/shader_testing differ diff --git a/shader_testing.h b/shader_testing.h new file mode 100644 index 0000000..785acff --- /dev/null +++ b/shader_testing.h @@ -0,0 +1,96 @@ + +#pragma once + +#include +#include + + +typedef uint32_t u32; + + +struct gl_uniform +{ + GLenum id; + GLenum data_type; +}; + +struct gl_buffer +{ + GLuint id; + GLenum data_type; +}; + +struct shader +{ + GLuint prog_id; + GLuint xforms_ubo_id; +}; + +struct transforms +{ + glm::mat4 view_xform; + glm::mat4 proj_xform; + glm::mat4 normal_xform; +}; + +enum glsl_layout +{ + POSITION, + NORMAL, + UV, + COLOR +}; + +struct mesh +{ + u32 num_vertices; + u32 num_indices; + glm::vec3* vertices; + glm::vec3* normals; + glm::vec3* uvs; + glm::vec3* colors; + u32* indices; +}; + +struct gl_mesh +{ + u32 num_indices; + GLuint vao_id; + GLuint tex_id; + GLenum draw_mode; + + glm::mat4* model_xform; + GLuint model_xform_id; + + GLuint vert_buf_id; + GLuint norm_buf_id; + GLuint uv_buf_id; + GLuint clr_buf_id; + GLuint idx_buf_id; +}; + +struct node; +struct entity; +struct light; +struct animation; + + +const float DEFAULT_FOV = 60.f; +const float NEAR_CLIP_PLANE = 5.f; +const float DEFAULT_ASPECT_RATIO = 16.f / 9.f; +const glm::vec3 DEFAULT_CAM_POS = { 0, 0, -30.f }; +const glm::vec3 DEFAULT_LOOK_POS = { 0, 0, 0 }; + + +bool loadShaderProgram(const char* vs, const char* fs, shader* s); +transforms initXformUBO(shader* s, + float fov = DEFAULT_FOV, + float near_clip_plane = NEAR_CLIP_PLANE, + float aspect_ratio = DEFAULT_ASPECT_RATIO, + glm::vec3 cam_pos = DEFAULT_CAM_POS, + glm::vec3 look_pos = DEFAULT_LOOK_POS); +mesh initCubeMesh(); +gl_mesh loadGLMesh(shader* s, const mesh& m, const glm::vec3& pos); +void loop(shader* s, gl_mesh* mesh_arr, u32 num_meshes); +void quit(shader* s); +