#include #include #include #include #include "asset.h" #include "entity.h" #include "dumbLog.h" #include "GLDebug.h" #include "shader.h" #include "types.h" #define UTIL_IMPLEMENTATION #include "util.h" struct SDLHandles { SDL_Window* window; SDL_GLContext sdl_gl_ctx; SDL_DisplayMode display_mode; }; // TODO: node/tree structure for entities struct Node; struct RenderGroup { shader_program* shader; u32 num_entites; u32 max_entities; Entity* entities; char* name; }; struct RenderState { transforms* xforms; // NOTE: would be part of camera in libTangerine SDLHandles* handles; GLContext* gl_ctx; Assets assets; MemoryArena* rg_arena; u32 num_render_groups; u32 max_render_groups; RenderGroup* render_groups; u32 num_lights; u32 max_lights; PointLight* lights; }; #define DEFAULT_ENTITY_COUNT 256 void initRenderGroup(RenderGroup* rg, MemoryArena* arena, shader_program* shader, u32 num_entities = DEFAULT_ENTITY_COUNT, const char* name = "") { rg->max_entities = num_entities; rg->shader = shader; rg->name = arenaCopyCStr(arena, name); rg->entities = ARENA_ALLOC(arena, Entity, num_entities); } void freeRenderGroup(RenderGroup* rg, MemoryArena* arena) { printf("%s(), should probably look into freeing arena memory?\n", __FUNCTION__); assert(0); } Entity* getFreeEntity(RenderGroup* rg) { if (rg->num_entites < rg->max_entities) return &rg->entities[rg->num_entites++]; printf("%s(), render group full\n", __FUNCTION__); return nullptr; } RenderGroup* getFreeRenderGroup(RenderState* rs) { if (rs->num_render_groups < rs->max_render_groups) return &rs->render_groups[rs->num_render_groups++]; printf("%s(), no free render group\n", __FUNCTION__); return nullptr; } #define DEFAULT_TEXTURE_COUNT 64 #define DEFAULT_SHADER_COUNT 64 #define DEFAULT_UBO_COUNT 32 GLContext* initGLContext(MemoryArena* arena) { GLContext* gl_ctx = ARENA_ALLOC(arena, GLContext, 1); gl_ctx->max_shaders = DEFAULT_SHADER_COUNT; gl_ctx->shaders = ARENA_ALLOC(arena, shader_program, DEFAULT_SHADER_COUNT); gl_ctx->max_ubos = DEFAULT_UBO_COUNT; gl_ctx->uniform_buffers = ARENA_ALLOC(arena, gl_buffer, gl_ctx->max_ubos); gl_ctx->max_textures = DEFAULT_TEXTURE_COUNT; gl_ctx->textures = ARENA_ALLOC(arena, GLTexture, gl_ctx->max_textures); return gl_ctx; } #define DEFAULT_MODEL_COUNT 256 #define DEFAULT_RENDER_GROUP_COUNT 256 RenderState* initRenderState(u32 max_models = DEFAULT_MODEL_COUNT, u32 max_textures = DEFAULT_TEXTURE_COUNT) { RenderState* rs = UTIL_ALLOC(1, RenderState); if (rs) { rs->assets.arena = arenaInit(DEFAULT_ARENA_SIZE); rs->assets.max_models = max_models; rs->assets.models = ARENA_ALLOC(rs->assets.arena, model, max_models); rs->assets.max_textures = max_textures; rs->assets.textures = ARENA_ALLOC(rs->assets.arena, util_image, max_textures); rs->rg_arena = arenaInit(DEFAULT_ARENA_SIZE); rs->max_render_groups = DEFAULT_RENDER_GROUP_COUNT; rs->render_groups = ARENA_ALLOC(rs->rg_arena, RenderGroup, DEFAULT_RENDER_GROUP_COUNT); rs->gl_ctx = initGLContext(rs->assets.arena); rs->xforms = ARENA_ALLOC(rs->assets.arena, transforms, 1); rs->handles = UTIL_ALLOC(1, SDLHandles); } return rs; } void freeRenderState(RenderState*& rs) { if (rs) { arenaFree(rs->assets.arena); utilSafeFree(rs->handles); utilSafeFree(rs); rs = nullptr; } } bool initGraphics(SDLHandles* handles) { handles->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, &handles->display_mode); handles->sdl_gl_ctx = SDL_GL_CreateContext(handles->window); if (!handles->sdl_gl_ctx) { 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 handles->window != nullptr; } bool addShaders(RenderState* rs) { if (!addShaderProgram(rs->assets.arena, rs->gl_ctx, "../data/shader.vert", "../data/shader.frag", "default")) { LOG(Error) << "error loading default shader\n"; return false; } if (!addShaderProgram(rs->assets.arena, rs->gl_ctx, "../data/colored_vertices.vert", "../data/colored_vertices.frag", "colored_vertices")) { LOG(Error) << "error loading colored_vertices shader\n"; return false; } if (!addShaderProgram(rs->assets.arena, rs->gl_ctx, "../data/debug.vert", "../data/debug.frag", "debug")) { LOG(Error) << "error loading debug shader\n"; return false; } return true; } bool loadScene(RenderState* rs) { shader_program* s_default = getShaderByName("default", rs->gl_ctx); shader_program* s_debug = getShaderByName("debug", rs->gl_ctx); if (!s_default || !s_debug) return false; // TODO: full lighting model model* tex_cube = getModelByPath(&rs->assets, "../data/textured_cube.gltf"); assert(tex_cube != nullptr); const mesh& texmesh = tex_cube->meshes[0]; GLVertexAttrib* db_pos_attrib = getVertexAttribByName(s_debug, "position"); GLVertexAttrib* db_normal_attrib = getVertexAttribByName(s_debug, "normal"); if (!db_pos_attrib || !db_normal_attrib) return false; GLBufferToAttribMapping debug_mappings[2] = { { "position", db_pos_attrib, texmesh.vertices }, { "normal", db_normal_attrib, texmesh.normals } }; const u32 NUM_CUBES = 4; glm::vec3 cube_locs[NUM_CUBES] = { glm::vec3(-10, 10, 0), glm::vec3(-10, -10, 0), glm::vec3( 10, 10, 0), glm::vec3( 10, -10, 0), }; RenderGroup* orange_cubes = getFreeRenderGroup(rs); assert(orange_cubes != nullptr); initRenderGroup(orange_cubes, rs->rg_arena, s_debug, NUM_CUBES, "debug_cubes"); for (u32 i = 0; i < NUM_CUBES; i++) { Entity* e = getFreeEntity(orange_cubes); assert(e != nullptr); if (!initEntity(e, rs->gl_ctx, rs->rg_arena, tex_cube, 2, debug_mappings, "debug_box")) { // FIXME: this doesn't actually do anything freeRenderGroup(orange_cubes, rs->rg_arena); return false; } setEntityPosition(e, cube_locs[i]); } RenderGroup* debug_cube_group = getFreeRenderGroup(rs); assert(debug_cube_group != nullptr); initRenderGroup(debug_cube_group, rs->rg_arena, s_default, 1, "orange_cubes"); Entity* e = getFreeEntity(debug_cube_group); assert(e != nullptr); GLVertexAttrib* df_pos_attrib = getVertexAttribByName(s_default, "position"); GLVertexAttrib* df_uv_attrib = getVertexAttribByName(s_default, "uv"); if (!df_pos_attrib || !df_uv_attrib) return false; GLBufferToAttribMapping default_mappings[2] = { { "position", df_pos_attrib, texmesh.vertices }, { "uvs", df_uv_attrib, texmesh.uvs } }; initEntity(e, rs->gl_ctx, rs->rg_arena, tex_cube, 2, default_mappings, "textured_box"); return true; } void loop(RenderState* rs) { 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); for (u32 i = 0; i < rs->num_render_groups; i++) { RenderGroup& rg = rs->render_groups[i]; for (u32 j = 0; j < rg.num_entites; j++) { Entity& e = rg.entities[j]; rotateEntity(&e, glm::vec3(0, 1, 0), (float) M_PI / 60); for (u32 k = 0; k < e.num_meshes; k++) { GLmesh& glm = e.meshes[k]; renderVAO(&glm, e.xform, rg.shader); } } } SDL_GL_SwapWindow(rs->handles->window); glUseProgram(0); frame_time = SDL_GetTicks() - frame_start; if (delay > frame_time) SDL_Delay(delay - frame_time); } } void quit(RenderState*& rs) { SDL_GL_DeleteContext(rs->handles->sdl_gl_ctx); SDL_DestroyWindow(rs->handles->window); SDL_Quit(); freeRenderState(rs); } int main() { RenderState* rs = initRenderState(); if (!initGraphics(rs->handles)) return 1; if (rs) { // FIXME: need to test this with another shader that has another UBO gl_buffer& ubo = rs->gl_ctx->uniform_buffers[rs->gl_ctx->num_ubos++]; initTransforms(rs->assets.arena, rs->xforms, &ubo, rs->gl_ctx); if (!addShaders(rs)) { LOG(Error) << "error loading shaders\n"; return 1; } if (!loadScene(rs)) { LOG(Error) << "error loading scene\n"; return 1; } loop(rs); quit(rs); return 0; } std::cout << "error loading scene\n"; return 1; }