You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
310 lines
7.2 KiB
310 lines
7.2 KiB
|
|
#include <cassert> |
|
|
|
#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 |
|
|
|
RenderState* |
|
initRenderState(u32 max_models, |
|
u32 max_textures, |
|
u32 max_shaders, |
|
u32 max_render_groups, |
|
u32 max_ubos) |
|
{ |
|
LOGF(Info, "Initializing Renderer\n"); |
|
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, Texture, 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); |
|
|
|
// FIXME: revisit for 'Scene' abstraction |
|
rs->max_lights = 32; |
|
rs->lights = ARENA_ALLOC(rs->rg_arena, PointLight, rs->max_lights); |
|
|
|
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 |
|
GLBuffer& ubo = rs->gl_ctx->uniform_buffers[rs->gl_ctx->num_ubos++]; |
|
initTransforms(rs->assets.arena, rs->xforms, &ubo, rs->gl_ctx); |
|
|
|
bool ret = loadDefaultShaders(rs); |
|
assert(ret); |
|
} |
|
|
|
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 |
|
initRenderGroup(RenderGroup* rg, |
|
MemoryArena* arena, |
|
ShaderProgram* 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) |
|
{ |
|
LOGF(Info, "should probably look into freeing arena memory?\n"); |
|
assert(0); |
|
} |
|
|
|
RenderGroup* |
|
getFreeRenderGroup(RenderState* rs) |
|
{ |
|
if (rs->num_render_groups < rs->max_render_groups) |
|
return &rs->render_groups[rs->num_render_groups++]; |
|
|
|
LOGF(Error, "no free render group\n"); |
|
return nullptr; |
|
} |
|
|
|
Entity* |
|
getFreeEntity(RenderGroup* rg) |
|
{ |
|
if (rg->num_entities < rg->max_entities) |
|
return &rg->entities[rg->num_entities++]; |
|
|
|
LOGF(Error, "render group full\n"); |
|
return nullptr; |
|
} |
|
|
|
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->model_xform, rg->shader, |
|
e->diffuse_texture->id); |
|
} |
|
} |
|
} |
|
} |
|
|
|
bool |
|
loadDefaultShaders(RenderState* rs, |
|
u32 num_shaders, |
|
const ShaderInit shaders[]) |
|
{ |
|
for (u32 i = 0; i < num_shaders; i++) { |
|
const ShaderInit& si = shaders[i]; |
|
|
|
if (!addShaderProgram(rs->assets.arena, |
|
rs->gl_ctx, |
|
si.vert_path, |
|
si.frag_path, |
|
si.name)) |
|
{ |
|
LOG(Error) << "failed to load shader " << si.name << "\n"; |
|
return false; |
|
} |
|
|
|
ShaderProgram* shader = getShaderByName(si.name, rs->gl_ctx); |
|
assert(shader); |
|
|
|
// NOTE: not every buffer will be available for every shader, so we |
|
// enumerate them all, and store the ones that are present |
|
u32 attrib_idx = 0; |
|
|
|
for (u32 i = 0; i < MESH_BUFFER_TYPE_COUNT; i++) { |
|
MeshBufferType buf_type = (MeshBufferType) i; |
|
GLVertexAttrib* attrib = getVertexAttribByType(shader, buf_type); |
|
|
|
if (attrib) |
|
shader->attrib_mappings[attrib_idx++] = { attrib, buf_type }; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
GLVertexAttrib* |
|
getVertexAttribByType(ShaderProgram* shader, MeshBufferType buf_type) |
|
{ |
|
switch (buf_type) { |
|
case VERTEX: return getVertexAttribByName(shader, "position"); |
|
case NORMAL: return getVertexAttribByName(shader, "normal"); |
|
case UV: return getVertexAttribByName(shader, "uv"); |
|
case COLOR: return getVertexAttribByName(shader, "color"); |
|
default: return nullptr; |
|
} |
|
} |
|
|
|
|
|
// 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, ShaderProgram, max_shaders); |
|
gl_ctx->max_ubos = max_ubos; |
|
gl_ctx->uniform_buffers = ARENA_ALLOC(arena, GLBuffer, max_ubos); |
|
gl_ctx->max_textures = max_textures; |
|
gl_ctx->textures = ARENA_ALLOC(arena, GLTexture, max_textures); |
|
|
|
return gl_ctx; |
|
} |
|
|
|
|