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