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.
408 lines
9.9 KiB
408 lines
9.9 KiB
|
|
#include <cassert> |
|
|
|
#define GLM_FORCE_XYZW_ONLY |
|
#include <glm/gtc/matrix_transform.hpp> |
|
|
|
#include "tangerine.h" |
|
#define UTIL_IMPLEMENTATION |
|
#include "util.h" |
|
#define GL_DEBUG_IMPLEMENTATION |
|
#include "GLDebug.h" |
|
|
|
|
|
// forward declarations |
|
|
|
bool initGraphics(SDLHandles* handles, const char* title, uvec2 dims); |
|
|
|
LightsBuffer* initLights(RenderState* rs, u32 max_lights, vec4 ambient_color); |
|
|
|
|
|
// interface |
|
|
|
RenderState* |
|
initRenderState(const char* window_title, |
|
uvec2 window_dims, |
|
u32 SDL_flags, |
|
GLClearColor clear_col, |
|
vec4 ambient_color, |
|
u32 max_models, |
|
u32 max_textures, |
|
u32 max_shaders, |
|
u32 max_render_groups, |
|
u32 max_ubos, |
|
u32 max_lights) |
|
{ |
|
LOGF(Info, "Initializing Renderer\n"); |
|
RenderState* rs = UTIL_ALLOC(1, RenderState); |
|
|
|
if (rs) { |
|
rs->clear_col = clear_col; |
|
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); |
|
rs->window_dims = window_dims; |
|
rs->handles.SDL_flags = SDL_flags; |
|
|
|
if (!initGraphics(&rs->handles, window_title, window_dims)) { |
|
LOGF(Error, "error initializing renderer\n"); |
|
return nullptr; |
|
} |
|
|
|
rs->gl_ctx = initGLContext(rs->assets.arena, |
|
max_shaders, |
|
max_textures, |
|
max_ubos); |
|
|
|
rs->camera = UTIL_ALLOC(1, Camera); |
|
GLBuffer* xforms_ubo = initGLBackingBuffer(rs->gl_ctx, |
|
rs->assets.arena, |
|
"matrices", |
|
GL_FLOAT, |
|
sizeof(Transforms), |
|
&rs->camera->xforms); |
|
assert(xforms_ubo); |
|
|
|
// FIXME: should this be an interface function? |
|
rs->lights_buf = initLights(rs, max_lights, ambient_color); |
|
|
|
// FIXME: should have an error message instead of assert here, and |
|
// clean up arenas/allocations before returning |
|
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); |
|
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; |
|
} |
|
|
|
RenderGroup* |
|
getRenderGroupByName(RenderState* rs, const char* name) |
|
{ |
|
RenderGroup* rg_out = nullptr; |
|
|
|
for (u32 i = 0; i < rs->num_render_groups; i++) { |
|
RenderGroup* rg = &rs->render_groups[i]; |
|
|
|
if (utilCStrMatch(name, rg->name)) |
|
rg_out = rg; |
|
} |
|
|
|
if (rg_out == nullptr) |
|
LOGF(Error, "render group with name, \"%s\", not found\n", name); |
|
|
|
return rg_out; |
|
} |
|
|
|
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; |
|
} |
|
|
|
Entity* |
|
getEntityByName(RenderGroup* rg, const char* name) |
|
{ |
|
Entity* e_out = nullptr; |
|
|
|
for (u32 i = 0; i < rg->num_entities; i++) { |
|
Entity* e = &rg->entities[i]; |
|
|
|
if (utilCStrMatch(name, e->name)) |
|
e_out = e; |
|
} |
|
|
|
if (e_out == nullptr) |
|
LOGF(Error, "Entity with name, \"%s\", not found\n", name); |
|
|
|
return e_out; |
|
} |
|
|
|
void |
|
doRenderLoop(RenderState* rs, |
|
u32 framerate, |
|
frame_callback_fn cb_func_pre, |
|
frame_callback_fn cb_func_post, |
|
void* user_data) |
|
{ |
|
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, user_data); |
|
} 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, user_data); |
|
|
|
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, e->draw_mode); |
|
} |
|
} |
|
} |
|
} |
|
|
|
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, const char* title, uvec2 dims) |
|
{ |
|
handles->window = SDL_CreateWindow( |
|
title, |
|
SDL_WINDOWPOS_CENTERED_DISPLAY(0), |
|
SDL_WINDOWPOS_CENTERED_DISPLAY(0), |
|
dims.x, |
|
dims.y, |
|
SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE); |
|
|
|
if (SDL_Init(handles->SDL_flags) != 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; |
|
} |
|
|
|
LightsBuffer* |
|
initLights(RenderState* rs, |
|
u32 max_lights, |
|
vec4 ambient_color) |
|
{ |
|
// FIXME: revisit for 'Scene' abstraction |
|
// FIXME: see if we can simplify this with the use of offsetof() |
|
// https://en.cppreference.com/w/cpp/types/offsetof |
|
|
|
LightsBuffer* lb = ARENA_ALLOC(rs->assets.arena, LightsBuffer, 1); |
|
lb->buf_size = 8 * sizeof(u32) // NOTE: 'header' |
|
+ sizeof(vec4) // NOTE: ambient color |
|
+ 6 * max_lights * sizeof(vec4); // NOTE: vector arrays |
|
LOGF(Debug, "buf_size: %d\n", lb->buf_size); |
|
lb->buffer = ARENA_ALLOC(rs->assets.arena, u8, lb->buf_size); |
|
|
|
lb->max_p_lights = (u32*) lb->buffer; |
|
lb->active_p_lights = |
|
(u32*) arenaGetAddressOffset(lb->max_p_lights, sizeof(u32)); |
|
lb->max_d_lights = |
|
(u32*) arenaGetAddressOffset(lb->active_p_lights, sizeof(u32)); |
|
lb->active_d_lights = |
|
(u32*) arenaGetAddressOffset(lb->max_d_lights, sizeof(u32)); |
|
|
|
*lb->max_p_lights = max_lights; |
|
*lb->max_d_lights = max_lights; |
|
|
|
// NOTE: add padding, we're not actually using this since 4 * u32 is on a |
|
// 16 byte boundary, but will be helpful if we need to add more 'headers' |
|
// in the future |
|
void* arr_start = arenaGetAddressOffset(lb->buffer, 8 * sizeof(u32)); |
|
|
|
// NOTE: ambient color |
|
lb->ambient_color = (vec4*) arr_start; |
|
*lb->ambient_color = ambient_color; |
|
|
|
// NOTE: set offsets for array pointers |
|
u32 arr_size = max_lights * sizeof(vec4); |
|
lb->pl_positions = //(vec4*) arr_start; |
|
(vec4*) arenaGetAddressOffset(arr_start, sizeof(vec4)); |
|
lb->pl_colors = |
|
(vec4*) arenaGetAddressOffset(lb->pl_positions, arr_size); |
|
lb->pl_intensities = |
|
(uvec4*) arenaGetAddressOffset(lb->pl_colors, arr_size); |
|
lb->dl_directions = |
|
(vec4*) arenaGetAddressOffset(lb->pl_intensities, arr_size); |
|
lb->dl_colors = |
|
(vec4*) arenaGetAddressOffset(lb->dl_directions, arr_size); |
|
lb->dl_intensities = |
|
(uvec4*) arenaGetAddressOffset(lb->dl_colors, arr_size); |
|
|
|
initGLBackingBuffer(rs->gl_ctx, |
|
rs->assets.arena, |
|
"lights", |
|
GL_BYTE, |
|
lb->buf_size, |
|
lb->buffer); |
|
|
|
return lb; |
|
} |
|
|
|
|