From 5c3760eab20910919de2dba000ab7bf074892c64 Mon Sep 17 00:00:00 2001 From: cinnaboot Date: Thu, 13 Jan 2022 11:19:30 -0500 Subject: [PATCH] clean up renderer initialization, and loop interface Now we match the style of libTangerine where we pass in callbacks to a doRenderLoop function, and handle SDL calls internally --- src/main.cpp | 300 +++------------------------------------------- src/tangerine.cpp | 254 +++++++++++++++++++++++++++++++++++++++ src/tangerine.h | 132 ++++++++++++++++++++ 3 files changed, 404 insertions(+), 282 deletions(-) create mode 100644 src/tangerine.cpp create mode 100644 src/tangerine.h diff --git a/src/main.cpp b/src/main.cpp index b93079b..5173cd7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,235 +1,8 @@ #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" - -/* -* === TODO: === -* - work on cleaner interface for initEntity and loadScene... -* - full lighting model -* - test complex entities -* - fix debug load times (either by using cgltf, or hiding tinygltf.h) -* - merge back into libTangerine -* -* === TODONE: === -* - don't allocate Asset structure on asset arena -* - store on RenderState as reference -* - store asset arena to asset structure -* - condense get_X_ByPath functions with assetLoad functions -* - move to asset interface -* - load diffuse texture automatically when loading a model -* - move entity structure and functions to new file? -* - load diffuse texture into OpenGL, and store GLTexture structure onto -* GLContext -* - need a cache algorithm starting in initEntity() -* - check for GLTexture by path hash -* - load texture into gl if not cached -* - pass result GLTexture to loadGLMesh() -* - update loadScene function with textured models, and test texture loading -*/ - - -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++]; +#include "tangerine.h" - 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); - arenaFree(rs->rg_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) @@ -353,74 +126,36 @@ loadScene(RenderState* rs) } void -loop(RenderState* rs) +render_cb_pre(RenderState* rs) { - u32 delay = 16; - 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; - } + while (SDL_PollEvent(&e)) { + if (e.type == SDL_QUIT || + (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE)) + { + rs->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 / (3 * 60)); + for (u32 i = 0; i < rs->num_render_groups; i++) { + RenderGroup& rg = rs->render_groups[i]; - for (u32 k = 0; k < e.num_meshes; k++) { - GLmesh& glm = e.meshes[k]; - renderVAO(&glm, e.xform, rg.shader, e.diffuse_texture->id); - } - } + for (u32 j = 0; j < rg.num_entities; j++) { + Entity& e = rg.entities[j]; + rotateEntity(&e, glm::vec3(0, 1, 0), (float) M_PI / (3 * 60)); } - - 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; + rs->clear_col = {0.2, 0.2, 0.2, 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; @@ -431,8 +166,9 @@ main() return 1; } - loop(rs); - quit(rs); + doRenderLoop(rs, 60, render_cb_pre, nullptr); + + freeRenderState(rs); return 0; } diff --git a/src/tangerine.cpp b/src/tangerine.cpp new file mode 100644 index 0000000..c054cd2 --- /dev/null +++ b/src/tangerine.cpp @@ -0,0 +1,254 @@ + +#include +#include // NOTE: printf, TODO: can remove when we implement LOGF() + +#include "tangerine.h" +#define UTIL_IMPLEMENTATION +#include "util.h" + + +// forward declarations + +bool initGraphics(SDLHandles* handles); + +GLContext* initGLContext(MemoryArena* arena, + u32 max_shaders, + u32 max_textures, + u32 max_ubos); + + +// interface + +void +initRenderGroup(RenderGroup* rg, + MemoryArena* arena, + shader_program* shader, + u32 num_entities, + 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_entities < rg->max_entities) + return &rg->entities[rg->num_entities++]; + + 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; +} + +RenderState* +initRenderState(u32 max_models, + u32 max_textures, + u32 max_shaders, + u32 max_render_groups, + u32 max_ubos) +{ + 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, + max_shaders, + max_textures, + max_ubos); + + rs->xforms = ARENA_ALLOC(rs->assets.arena, transforms, 1); + rs->handles = UTIL_ALLOC(1, SDLHandles); + initGraphics(rs->handles); + + // 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); + } + + return rs; +} + +void +freeRenderState(RenderState*& rs) +{ + if (rs) { + SDL_GL_DeleteContext(rs->handles->sdl_gl_ctx); + SDL_DestroyWindow(rs->handles->window); + arenaFree(rs->assets.arena); + arenaFree(rs->rg_arena); + utilSafeFree(rs->handles); + utilSafeFree(rs); + rs = nullptr; + } + + SDL_Quit(); +} + +void +doRenderLoop(RenderState* rs, + u32 framerate, + frame_callback_fn cb_func_pre, + frame_callback_fn cb_func_post) +{ + u32 delay = (framerate > 0) ? 1 / framerate : 0; + u32 frameStart, frameTime; + rs->running = true; + SDL_Event e; + + while (rs->running) { + frameStart = SDL_GetTicks(); + + if (cb_func_pre != nullptr) { + cb_func_pre(rs); + } else { + while (SDL_PollEvent(&e)) { + if (e.type == SDL_QUIT || + (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE)) + { + rs->running = false; + break; + } + } + } + + renderFrame(rs, rs->clear_col); + + if (cb_func_post != nullptr) + cb_func_post(rs); + + SDL_GL_SwapWindow(rs->handles->window); + frameTime = SDL_GetTicks() - frameStart; + + if (delay > frameTime) + SDL_Delay(delay - frameTime); + } +} + +void +renderFrame(RenderState* rs, const GLClearColor& clear_col) +{ + glClearColor(clear_col.R, + clear_col.G, + clear_col.B, + clear_col.A); + 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_entities; j++) { + Entity* e = &rg->entities[j]; + + for (u32 k = 0; k < e->num_meshes; k++) { + GLmesh& glm = e->meshes[k]; + renderVAO(&glm, e->xform, rg->shader, e->diffuse_texture->id); + } + } + } +} + + +// internal + +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; +} + +GLContext* +initGLContext(MemoryArena* arena, + u32 max_shaders, + u32 max_textures, + u32 max_ubos) +{ + GLContext* gl_ctx = ARENA_ALLOC(arena, GLContext, 1); + gl_ctx->max_shaders = max_shaders; + gl_ctx->shaders = ARENA_ALLOC(arena, shader_program, max_shaders); + gl_ctx->max_ubos = max_ubos; + gl_ctx->uniform_buffers = ARENA_ALLOC(arena, gl_buffer, max_ubos); + gl_ctx->max_textures = max_textures; + gl_ctx->textures = ARENA_ALLOC(arena, GLTexture, max_textures); + + return gl_ctx; +} + diff --git a/src/tangerine.h b/src/tangerine.h new file mode 100644 index 0000000..69d496e --- /dev/null +++ b/src/tangerine.h @@ -0,0 +1,132 @@ + +#pragma once + +#include + +#include "asset.h" +#include "entity.h" +#include "dumbLog.h" +#include "GLDebug.h" +#include "shader.h" +#include "types.h" +//#include "util.h" + + +/* +* === TODO: === +* - work on cleaner interface for initEntity and loadScene... +* - full lighting model +* - test complex entities +* - need a separate GLBufferToAttribMapping for each mesh on an entity +* - fix debug load times (either by using cgltf, or hiding tinygltf.h) +* - add libTangerine namespace? +* - merge back into libTangerine +* +* === TODONE: === +* - don't allocate Asset structure on asset arena +* - store on RenderState as reference +* - store asset arena to asset structure +* - condense get_X_ByPath functions with assetLoad functions +* - move to asset interface +* - load diffuse texture automatically when loading a model +* - move entity structure and functions to new file? +* - load diffuse texture into OpenGL, and store GLTexture structure onto +* GLContext +* - need a cache algorithm starting in initEntity() +* - check for GLTexture by path hash +* - load texture into gl if not cached +* - pass result GLTexture to loadGLMesh() +* - update loadScene function with textured models, and test texture loading +*/ + + +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_entities; + u32 max_entities; + Entity* entities; + char* name; +}; + +struct GLClearColor +{ + GLfloat R; + GLfloat G; + GLfloat B; + GLfloat A; +}; + +struct RenderState +{ + bool running; + 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; + + GLClearColor clear_col; + // TODO: full lighting +#if 0 + u32 num_lights; + u32 max_lights; + PointLight* lights; +#endif +}; + + +#define DEFAULT_MODEL_COUNT 256 +#define DEFAULT_TEXTURE_COUNT 64 +#define DEFAULT_SHADER_COUNT 64 +#define DEFAULT_UBO_COUNT 32 +#define DEFAULT_RENDER_GROUP_COUNT 256 +RenderState* initRenderState(u32 max_models = DEFAULT_MODEL_COUNT, + u32 max_textures = DEFAULT_TEXTURE_COUNT, + u32 max_shaders = DEFAULT_SHADER_COUNT, + u32 max_render_groups = DEFAULT_RENDER_GROUP_COUNT, + u32 max_ubos = DEFAULT_UBO_COUNT); + +void freeRenderState(RenderState*& rs); + +RenderGroup* getFreeRenderGroup(RenderState* rs); + +#define DEFAULT_ENTITY_COUNT 256 +void initRenderGroup(RenderGroup* rg, + MemoryArena* arena, + shader_program* shader, + u32 num_entities = DEFAULT_ENTITY_COUNT, + const char* name = ""); + +void freeRenderGroup(RenderGroup* rg, MemoryArena* arena); + +Entity* getFreeEntity(RenderGroup* rg); + +// NOTE: callback function signature to use with renDoRenderLoop() +typedef void (*frame_callback_fn) (RenderState*); + +// NOTE: There are 2 callbacks to use here, cb_func_pre is called before +// the call to renRenderFrame(), cb_fun_post is called after +// NOTE: if you use cb_func_pre, you will have to use SDL_PollEvent() manually +// and at minimum set rs->running = false on SDL_QUIT event +void doRenderLoop(RenderState* rs, + uint framerate = 60, + frame_callback_fn cb_func_pre = nullptr, + frame_callback_fn cb_func_post = nullptr); + +void renderFrame(RenderState* rs, const GLClearColor& clear_col); +