Browse Source

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
main
cinnaboot 5 years ago
parent
commit
5c3760eab2
  1. 300
      src/main.cpp
  2. 254
      src/tangerine.cpp
  3. 132
      src/tangerine.h

300
src/main.cpp

@ -1,235 +1,8 @@
#include <cassert>
#include <cstring>
#include <iostream>
#include <SDL2/SDL.h>
#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;
}

254
src/tangerine.cpp

@ -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;
}

132
src/tangerine.h

@ -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…
Cancel
Save