#include #include #include #include #include #include "asset.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 Entity { u32 num_meshes; GLmesh* meshes; GLTexture* diffuse_texture; // NOTE: pointer into gl_ctx->textures array glm::mat4* xform; char* name; }; 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; memory_arena* asset_arena; Assets* assets; memory_arena* 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, memory_arena* 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, memory_arena* 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; } bool initEntity(Entity* e, memory_arena* arena, model* mdl, u32 num_attrib_mappings, GLBufferToAttribMapping* attrib_mappings, const char* name = "") { e->num_meshes = mdl->num_meshes; e->meshes = ARENA_ALLOC(arena, GLmesh, e->num_meshes); e->xform = ARENA_ALLOC(arena, glm::mat4, 1); *e->xform = glm::mat4(1.f); e->name = arenaCopyCStr(arena, name); for (u32 i = 0; i< e->num_meshes; i++) { GLmesh* glm = &e->meshes[i]; *glm = loadGLMesh(mdl->meshes[i], GL_TRIANGLES, glm::vec3(0, 0, 0), num_attrib_mappings, attrib_mappings); if (glm->vao_id == 0) { printf("%s(), error initializing entity\n", __FUNCTION__); return false; } } return true; } void setEntityPosition(Entity* e, glm::vec3 pos) { *e->xform = glm::translate(*e->xform, pos); } void rotateEntity(Entity* e, glm::vec3 axis, float radians) { *e->xform = glm::rotate(*e->xform, radians, axis); } // NOTE: equivalent to rgAppend() in libTangerine model* getModelByPath(RenderState* rs, const char* filepath) { model* mdl = assetGetCached(rs->assets, utilFNV64a_str(filepath)); if (!mdl) mdl = assetLoadFromFile(rs->assets, rs->asset_arena, filepath); return mdl; } 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_MODEL_COUNT 256 #define DEFAULT_TEXTURE_COUNT 64 Assets* initAssets(memory_arena* arena, u32 num_models = DEFAULT_MODEL_COUNT, u32 num_textures = DEFAULT_TEXTURE_COUNT) { Assets* assets = ARENA_ALLOC(arena, Assets, 1); assets->max_models = num_models; assets->models = ARENA_ALLOC(arena, model, num_models); assets->max_textures = num_textures; assets->textures = ARENA_ALLOC(arena, util_image, num_textures); return assets; } #define DEFAULT_SHADER_COUNT 64 #define DEFAULT_UBO_COUNT 32 GLContext* initGLContext(memory_arena* 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_RENDER_GROUP_COUNT 256 RenderState* initRenderState() { RenderState* rs = UTIL_ALLOC(1, RenderState); if (rs) { rs->asset_arena = arenaInit(DEFAULT_ARENA_SIZE); rs->assets = initAssets(rs->asset_arena); 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->asset_arena); rs->xforms = ARENA_ALLOC(rs->asset_arena, transforms, 1); rs->handles = UTIL_ALLOC(1, SDLHandles); } return rs; } void freeRenderState(RenderState*& rs) { if (rs) { arenaFree(rs->asset_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->asset_arena, rs->gl_ctx, "../data/shader.vert", "../data/shader.frag", "default")) { LOG(Error) << "error loading default shader\n"; return false; } if (!addShaderProgram(rs->asset_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->asset_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, "../data/textured_cube.gltf"); assert(tex_cube != nullptr && tex_cube->num_meshes > 0); const mesh& texmesh = tex_cube->meshes[0]; GLVertexAttrib* pos_attrib = getVertexAttribByName(s_debug, "position"); GLVertexAttrib* normal_attrib = getVertexAttribByName(s_debug, "normal"); if (!pos_attrib || !normal_attrib) return false; GLBufferToAttribMapping attrib_mappings[2] = { { "position", pos_attrib, texmesh.vertices }, { "normal", 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->rg_arena, tex_cube, 2, attrib_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); initEntity(e, rs->rg_arena, tex_cube, 2, attrib_mappings, "orange_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->asset_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; }