Browse Source
Now we match the style of libTangerine where we pass in callbacks to a doRenderLoop function, and handle SDL calls internallymain
3 changed files with 404 additions and 282 deletions
@ -0,0 +1,254 @@ |
|||||||
|
|
||||||
|
#include <cassert> |
||||||
|
#include <cstdio> // 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; |
||||||
|
} |
||||||
|
|
||||||
@ -0,0 +1,132 @@ |
|||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <SDL2/SDL.h> |
||||||
|
|
||||||
|
#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); |
||||||
|
|
||||||
Loading…
Reference in new issue